blob: b86c020bb3036243152f4d29afaaa3cd91391f3b [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))
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000304 eq(b.__rsub__(a), td(6, 24*3600 - 60))
Tim Peters2a799bf2002-12-16 20:18:38 +0000305 eq(-a, td(-7))
306 eq(+a, td(7))
307 eq(-b, td(-1, 24*3600 - 60))
308 eq(-c, td(-1, 24*3600 - 1, 999000))
309 eq(abs(a), a)
310 eq(abs(-a), a)
311 eq(td(6, 24*3600), a)
312 eq(td(0, 0, 60*1000000), b)
313 eq(a*10, td(70))
314 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000315 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000316 eq(b*10, td(0, 600))
317 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000318 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000319 eq(c*10, td(0, 0, 10000))
320 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000321 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000322 eq(a*-1, -a)
323 eq(b*-2, -b-b)
324 eq(c*-2, -c+-c)
325 eq(b*(60*24), (b*60)*24)
326 eq(b*(60*24), (60*b)*24)
327 eq(c*1000, td(0, 1))
328 eq(1000*c, td(0, 1))
329 eq(a//7, td(1))
330 eq(b//10, td(0, 6))
331 eq(c//1000, td(0, 0, 1))
332 eq(a//10, td(0, 7*24*360))
333 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000334 eq(a/0.5, td(14))
335 eq(b/0.5, td(0, 120))
336 eq(a/7, td(1))
337 eq(b/10, td(0, 6))
338 eq(c/1000, td(0, 0, 1))
339 eq(a/10, td(0, 7*24*360))
340 eq(a/3600000, td(0, 0, 7*24*1000))
341
342 # Multiplication by float
343 us = td(microseconds=1)
344 eq((3*us) * 0.5, 2*us)
345 eq((5*us) * 0.5, 2*us)
346 eq(0.5 * (3*us), 2*us)
347 eq(0.5 * (5*us), 2*us)
348 eq((-3*us) * 0.5, -2*us)
349 eq((-5*us) * 0.5, -2*us)
350
351 # Division by int and float
352 eq((3*us) / 2, 2*us)
353 eq((5*us) / 2, 2*us)
354 eq((-3*us) / 2.0, -2*us)
355 eq((-5*us) / 2.0, -2*us)
356 eq((3*us) / -2, -2*us)
357 eq((5*us) / -2, -2*us)
358 eq((3*us) / -2.0, -2*us)
359 eq((5*us) / -2.0, -2*us)
360 for i in range(-10, 10):
361 eq((i*us/3)//us, round(i/3))
362 for i in range(-10, 10):
363 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000364
365 def test_disallowed_computations(self):
366 a = timedelta(42)
367
Mark Dickinson5c2db372009-12-05 20:28:34 +0000368 # Add/sub ints or floats should be illegal
369 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000370 self.assertRaises(TypeError, lambda: a+i)
371 self.assertRaises(TypeError, lambda: a-i)
372 self.assertRaises(TypeError, lambda: i+a)
373 self.assertRaises(TypeError, lambda: i-a)
374
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000375 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000376 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000377 zero = 0
378 self.assertRaises(TypeError, lambda: zero // a)
379 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000380 self.assertRaises(ZeroDivisionError, lambda: a / zero)
381 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000382 self.assertRaises(TypeError, lambda: a / '')
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000383
384 @requires_IEEE_754
385 def test_disallowed_special(self):
386 a = timedelta(42)
387 self.assertRaises(ValueError, a.__mul__, NAN)
388 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000389
390 def test_basic_attributes(self):
391 days, seconds, us = 1, 7, 31
392 td = timedelta(days, seconds, us)
393 self.assertEqual(td.days, days)
394 self.assertEqual(td.seconds, seconds)
395 self.assertEqual(td.microseconds, us)
396
Antoine Pitroube6859d2009-11-25 23:02:32 +0000397 def test_total_seconds(self):
398 td = timedelta(days=365)
399 self.assertEqual(td.total_seconds(), 31536000.0)
400 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
401 td = timedelta(seconds=total_seconds)
402 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000403 # Issue8644: Test that td.total_seconds() has the same
404 # accuracy as td / timedelta(seconds=1).
405 for ms in [-1, -2, -123]:
406 td = timedelta(microseconds=ms)
407 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000408
Tim Peters2a799bf2002-12-16 20:18:38 +0000409 def test_carries(self):
410 t1 = timedelta(days=100,
411 weeks=-7,
412 hours=-24*(100-49),
413 minutes=-3,
414 seconds=12,
415 microseconds=(3*60 - 12) * 1e6 + 1)
416 t2 = timedelta(microseconds=1)
417 self.assertEqual(t1, t2)
418
419 def test_hash_equality(self):
420 t1 = timedelta(days=100,
421 weeks=-7,
422 hours=-24*(100-49),
423 minutes=-3,
424 seconds=12,
425 microseconds=(3*60 - 12) * 1000000)
426 t2 = timedelta()
427 self.assertEqual(hash(t1), hash(t2))
428
429 t1 += timedelta(weeks=7)
430 t2 += timedelta(days=7*7)
431 self.assertEqual(t1, t2)
432 self.assertEqual(hash(t1), hash(t2))
433
434 d = {t1: 1}
435 d[t2] = 2
436 self.assertEqual(len(d), 1)
437 self.assertEqual(d[t1], 2)
438
439 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000440 args = 12, 34, 56
441 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000442 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000443 green = pickler.dumps(orig, proto)
444 derived = unpickler.loads(green)
445 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000446
447 def test_compare(self):
448 t1 = timedelta(2, 3, 4)
449 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000450 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000451 self.assertTrue(t1 <= t2)
452 self.assertTrue(t1 >= t2)
453 self.assertTrue(not t1 != t2)
454 self.assertTrue(not t1 < t2)
455 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000456
457 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
458 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000459 self.assertTrue(t1 < t2)
460 self.assertTrue(t2 > t1)
461 self.assertTrue(t1 <= t2)
462 self.assertTrue(t2 >= t1)
463 self.assertTrue(t1 != t2)
464 self.assertTrue(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)
469 self.assertTrue(not t1 >= t2)
470 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000471
Tim Peters68124bb2003-02-08 03:46:31 +0000472 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000473 self.assertEqual(t1 == badarg, False)
474 self.assertEqual(t1 != badarg, True)
475 self.assertEqual(badarg == t1, False)
476 self.assertEqual(badarg != t1, True)
477
Tim Peters2a799bf2002-12-16 20:18:38 +0000478 self.assertRaises(TypeError, lambda: t1 <= badarg)
479 self.assertRaises(TypeError, lambda: t1 < badarg)
480 self.assertRaises(TypeError, lambda: t1 > badarg)
481 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000482 self.assertRaises(TypeError, lambda: badarg <= t1)
483 self.assertRaises(TypeError, lambda: badarg < t1)
484 self.assertRaises(TypeError, lambda: badarg > t1)
485 self.assertRaises(TypeError, lambda: badarg >= t1)
486
487 def test_str(self):
488 td = timedelta
489 eq = self.assertEqual
490
491 eq(str(td(1)), "1 day, 0:00:00")
492 eq(str(td(-1)), "-1 day, 0:00:00")
493 eq(str(td(2)), "2 days, 0:00:00")
494 eq(str(td(-2)), "-2 days, 0:00:00")
495
496 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
497 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
498 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
499 "-210 days, 23:12:34")
500
501 eq(str(td(milliseconds=1)), "0:00:00.001000")
502 eq(str(td(microseconds=3)), "0:00:00.000003")
503
504 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
505 microseconds=999999)),
506 "999999999 days, 23:59:59.999999")
507
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000508 def test_repr(self):
509 name = 'datetime.' + self.theclass.__name__
510 self.assertEqual(repr(self.theclass(1)),
511 "%s(1)" % name)
512 self.assertEqual(repr(self.theclass(10, 2)),
513 "%s(10, 2)" % name)
514 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
515 "%s(-10, 2, 400000)" % name)
516
Tim Peters2a799bf2002-12-16 20:18:38 +0000517 def test_roundtrip(self):
518 for td in (timedelta(days=999999999, hours=23, minutes=59,
519 seconds=59, microseconds=999999),
520 timedelta(days=-999999999),
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000521 timedelta(days=-999999999, seconds=1),
Tim Peters2a799bf2002-12-16 20:18:38 +0000522 timedelta(days=1, seconds=2, microseconds=3)):
523
524 # Verify td -> string -> td identity.
525 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000526 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000527 s = s[9:]
528 td2 = eval(s)
529 self.assertEqual(td, td2)
530
531 # Verify identity via reconstructing from pieces.
532 td2 = timedelta(td.days, td.seconds, td.microseconds)
533 self.assertEqual(td, td2)
534
535 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000536 self.assertIsInstance(timedelta.min, timedelta)
537 self.assertIsInstance(timedelta.max, timedelta)
538 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000539 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000540 self.assertEqual(timedelta.min, timedelta(-999999999))
541 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
542 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
543
544 def test_overflow(self):
545 tiny = timedelta.resolution
546
547 td = timedelta.min + tiny
548 td -= tiny # no problem
549 self.assertRaises(OverflowError, td.__sub__, tiny)
550 self.assertRaises(OverflowError, td.__add__, -tiny)
551
552 td = timedelta.max - tiny
553 td += tiny # no problem
554 self.assertRaises(OverflowError, td.__add__, tiny)
555 self.assertRaises(OverflowError, td.__sub__, -tiny)
556
557 self.assertRaises(OverflowError, lambda: -timedelta.max)
558
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000559 day = timedelta(1)
560 self.assertRaises(OverflowError, day.__mul__, 10**9)
561 self.assertRaises(OverflowError, day.__mul__, 1e9)
562 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
563 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
564 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
565
566 @requires_IEEE_754
567 def _test_overflow_special(self):
568 day = timedelta(1)
569 self.assertRaises(OverflowError, day.__mul__, INF)
570 self.assertRaises(OverflowError, day.__mul__, -INF)
571
Tim Peters2a799bf2002-12-16 20:18:38 +0000572 def test_microsecond_rounding(self):
573 td = timedelta
574 eq = self.assertEqual
575
576 # Single-field rounding.
577 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
578 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
579 eq(td(milliseconds=0.6/1000), td(microseconds=1))
580 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
581
582 # Rounding due to contributions from more than one field.
583 us_per_hour = 3600e6
584 us_per_day = us_per_hour * 24
585 eq(td(days=.4/us_per_day), td(0))
586 eq(td(hours=.2/us_per_hour), td(0))
587 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
588
589 eq(td(days=-.4/us_per_day), td(0))
590 eq(td(hours=-.2/us_per_hour), td(0))
591 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
592
593 def test_massive_normalization(self):
594 td = timedelta(microseconds=-1)
595 self.assertEqual((td.days, td.seconds, td.microseconds),
596 (-1, 24*3600-1, 999999))
597
598 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000599 self.assertTrue(timedelta(1))
600 self.assertTrue(timedelta(0, 1))
601 self.assertTrue(timedelta(0, 0, 1))
602 self.assertTrue(timedelta(microseconds=1))
603 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000604
Tim Petersb0c854d2003-05-17 15:57:00 +0000605 def test_subclass_timedelta(self):
606
607 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000608 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000609 def from_td(td):
610 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000611
612 def as_hours(self):
613 sum = (self.days * 24 +
614 self.seconds / 3600.0 +
615 self.microseconds / 3600e6)
616 return round(sum)
617
618 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000619 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000620 self.assertEqual(t1.as_hours(), 24)
621
622 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000623 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000624 self.assertEqual(t2.as_hours(), -25)
625
626 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000627 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000628 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000629 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000630 self.assertEqual(t3.days, t4.days)
631 self.assertEqual(t3.seconds, t4.seconds)
632 self.assertEqual(t3.microseconds, t4.microseconds)
633 self.assertEqual(str(t3), str(t4))
634 self.assertEqual(t4.as_hours(), -1)
635
Mark Dickinson7c186e22010-04-20 22:32:49 +0000636 def test_division(self):
637 t = timedelta(hours=1, minutes=24, seconds=19)
638 second = timedelta(seconds=1)
639 self.assertEqual(t / second, 5059.0)
640 self.assertEqual(t // second, 5059)
641
642 t = timedelta(minutes=2, seconds=30)
643 minute = timedelta(minutes=1)
644 self.assertEqual(t / minute, 2.5)
645 self.assertEqual(t // minute, 2)
646
647 zerotd = timedelta(0)
648 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
649 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
650
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000651 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000652 # note: floor division of a timedelta by an integer *is*
653 # currently permitted.
654
655 def test_remainder(self):
656 t = timedelta(minutes=2, seconds=30)
657 minute = timedelta(minutes=1)
658 r = t % minute
659 self.assertEqual(r, timedelta(seconds=30))
660
661 t = timedelta(minutes=-2, seconds=30)
662 r = t % minute
663 self.assertEqual(r, timedelta(seconds=30))
664
665 zerotd = timedelta(0)
666 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
667
668 self.assertRaises(TypeError, mod, t, 10)
669
670 def test_divmod(self):
671 t = timedelta(minutes=2, seconds=30)
672 minute = timedelta(minutes=1)
673 q, r = divmod(t, minute)
674 self.assertEqual(q, 2)
675 self.assertEqual(r, timedelta(seconds=30))
676
677 t = timedelta(minutes=-2, seconds=30)
678 q, r = divmod(t, minute)
679 self.assertEqual(q, -2)
680 self.assertEqual(r, timedelta(seconds=30))
681
682 zerotd = timedelta(0)
683 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
684
685 self.assertRaises(TypeError, divmod, t, 10)
686
687
Tim Peters2a799bf2002-12-16 20:18:38 +0000688#############################################################################
689# date tests
690
691class TestDateOnly(unittest.TestCase):
692 # Tests here won't pass if also run on datetime objects, so don't
693 # subclass this to test datetimes too.
694
695 def test_delta_non_days_ignored(self):
696 dt = date(2000, 1, 2)
697 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
698 microseconds=5)
699 days = timedelta(delta.days)
700 self.assertEqual(days, timedelta(1))
701
702 dt2 = dt + delta
703 self.assertEqual(dt2, dt + days)
704
705 dt2 = delta + dt
706 self.assertEqual(dt2, dt + days)
707
708 dt2 = dt - delta
709 self.assertEqual(dt2, dt - days)
710
711 delta = -delta
712 days = timedelta(delta.days)
713 self.assertEqual(days, timedelta(-2))
714
715 dt2 = dt + delta
716 self.assertEqual(dt2, dt + days)
717
718 dt2 = delta + dt
719 self.assertEqual(dt2, dt + days)
720
721 dt2 = dt - delta
722 self.assertEqual(dt2, dt - days)
723
Tim Peters604c0132004-06-07 23:04:33 +0000724class SubclassDate(date):
725 sub_var = 1
726
Guido van Rossumd8faa362007-04-27 19:54:29 +0000727class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000728 # Tests here should pass for both dates and datetimes, except for a
729 # few tests that TestDateTime overrides.
730
731 theclass = date
732
733 def test_basic_attributes(self):
734 dt = self.theclass(2002, 3, 1)
735 self.assertEqual(dt.year, 2002)
736 self.assertEqual(dt.month, 3)
737 self.assertEqual(dt.day, 1)
738
739 def test_roundtrip(self):
740 for dt in (self.theclass(1, 2, 3),
741 self.theclass.today()):
742 # Verify dt -> string -> date identity.
743 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000744 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000745 s = s[9:]
746 dt2 = eval(s)
747 self.assertEqual(dt, dt2)
748
749 # Verify identity via reconstructing from pieces.
750 dt2 = self.theclass(dt.year, dt.month, dt.day)
751 self.assertEqual(dt, dt2)
752
753 def test_ordinal_conversions(self):
754 # Check some fixed values.
755 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
756 (1, 12, 31, 365),
757 (2, 1, 1, 366),
758 # first example from "Calendrical Calculations"
759 (1945, 11, 12, 710347)]:
760 d = self.theclass(y, m, d)
761 self.assertEqual(n, d.toordinal())
762 fromord = self.theclass.fromordinal(n)
763 self.assertEqual(d, fromord)
764 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000765 # if we're checking something fancier than a date, verify
766 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000767 self.assertEqual(fromord.hour, 0)
768 self.assertEqual(fromord.minute, 0)
769 self.assertEqual(fromord.second, 0)
770 self.assertEqual(fromord.microsecond, 0)
771
Tim Peters0bf60bd2003-01-08 20:40:01 +0000772 # Check first and last days of year spottily across the whole
773 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000774 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000775 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
776 d = self.theclass(year, 1, 1)
777 n = d.toordinal()
778 d2 = self.theclass.fromordinal(n)
779 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000780 # Verify that moving back a day gets to the end of year-1.
781 if year > 1:
782 d = self.theclass.fromordinal(n-1)
783 d2 = self.theclass(year-1, 12, 31)
784 self.assertEqual(d, d2)
785 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000786
787 # Test every day in a leap-year and a non-leap year.
788 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
789 for year, isleap in (2000, True), (2002, False):
790 n = self.theclass(year, 1, 1).toordinal()
791 for month, maxday in zip(range(1, 13), dim):
792 if month == 2 and isleap:
793 maxday += 1
794 for day in range(1, maxday+1):
795 d = self.theclass(year, month, day)
796 self.assertEqual(d.toordinal(), n)
797 self.assertEqual(d, self.theclass.fromordinal(n))
798 n += 1
799
800 def test_extreme_ordinals(self):
801 a = self.theclass.min
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 a = self.theclass.max
814 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
815 aord = a.toordinal()
816 b = a.fromordinal(aord)
817 self.assertEqual(a, b)
818
819 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
820
821 b = a - timedelta(days=1)
822 self.assertEqual(b.toordinal(), aord - 1)
823 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
824
825 def test_bad_constructor_arguments(self):
826 # bad years
827 self.theclass(MINYEAR, 1, 1) # no exception
828 self.theclass(MAXYEAR, 1, 1) # no exception
829 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
830 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
831 # bad months
832 self.theclass(2000, 1, 1) # no exception
833 self.theclass(2000, 12, 1) # no exception
834 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
835 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
836 # bad days
837 self.theclass(2000, 2, 29) # no exception
838 self.theclass(2004, 2, 29) # no exception
839 self.theclass(2400, 2, 29) # no exception
840 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
841 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
842 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
843 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
844 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
845 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
846
847 def test_hash_equality(self):
848 d = self.theclass(2000, 12, 31)
849 # same thing
850 e = self.theclass(2000, 12, 31)
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 d = self.theclass(2001, 1, 1)
861 # same thing
862 e = self.theclass(2001, 1, 1)
863 self.assertEqual(d, e)
864 self.assertEqual(hash(d), hash(e))
865
866 dic = {d: 1}
867 dic[e] = 2
868 self.assertEqual(len(dic), 1)
869 self.assertEqual(dic[d], 2)
870 self.assertEqual(dic[e], 2)
871
872 def test_computations(self):
873 a = self.theclass(2002, 1, 31)
874 b = self.theclass(1956, 1, 31)
875
876 diff = a-b
877 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
878 self.assertEqual(diff.seconds, 0)
879 self.assertEqual(diff.microseconds, 0)
880
881 day = timedelta(1)
882 week = timedelta(7)
883 a = self.theclass(2002, 3, 2)
884 self.assertEqual(a + day, self.theclass(2002, 3, 3))
885 self.assertEqual(day + a, self.theclass(2002, 3, 3))
886 self.assertEqual(a - day, self.theclass(2002, 3, 1))
887 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
888 self.assertEqual(a + week, self.theclass(2002, 3, 9))
889 self.assertEqual(a - week, self.theclass(2002, 2, 23))
890 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
891 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
892 self.assertEqual((a + week) - a, week)
893 self.assertEqual((a + day) - a, day)
894 self.assertEqual((a - week) - a, -week)
895 self.assertEqual((a - day) - a, -day)
896 self.assertEqual(a - (a + week), -week)
897 self.assertEqual(a - (a + day), -day)
898 self.assertEqual(a - (a - week), week)
899 self.assertEqual(a - (a - day), day)
900
Mark Dickinson5c2db372009-12-05 20:28:34 +0000901 # Add/sub ints or floats should be illegal
902 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000903 self.assertRaises(TypeError, lambda: a+i)
904 self.assertRaises(TypeError, lambda: a-i)
905 self.assertRaises(TypeError, lambda: i+a)
906 self.assertRaises(TypeError, lambda: i-a)
907
908 # delta - date is senseless.
909 self.assertRaises(TypeError, lambda: day - a)
910 # mixing date and (delta or date) via * or // is senseless
911 self.assertRaises(TypeError, lambda: day * a)
912 self.assertRaises(TypeError, lambda: a * day)
913 self.assertRaises(TypeError, lambda: day // a)
914 self.assertRaises(TypeError, lambda: a // day)
915 self.assertRaises(TypeError, lambda: a * a)
916 self.assertRaises(TypeError, lambda: a // a)
917 # date + date is senseless
918 self.assertRaises(TypeError, lambda: a + a)
919
920 def test_overflow(self):
921 tiny = self.theclass.resolution
922
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000923 for delta in [tiny, timedelta(1), timedelta(2)]:
924 dt = self.theclass.min + delta
925 dt -= delta # no problem
926 self.assertRaises(OverflowError, dt.__sub__, delta)
927 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000928
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000929 dt = self.theclass.max - delta
930 dt += delta # no problem
931 self.assertRaises(OverflowError, dt.__add__, delta)
932 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000933
934 def test_fromtimestamp(self):
935 import time
936
937 # Try an arbitrary fixed value.
938 year, month, day = 1999, 9, 19
939 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
940 d = self.theclass.fromtimestamp(ts)
941 self.assertEqual(d.year, year)
942 self.assertEqual(d.month, month)
943 self.assertEqual(d.day, day)
944
Tim Peters1b6f7a92004-06-20 02:50:16 +0000945 def test_insane_fromtimestamp(self):
946 # It's possible that some platform maps time_t to double,
947 # and that this test will fail there. This test should
948 # exempt such platforms (provided they return reasonable
949 # results!).
950 for insane in -1e200, 1e200:
951 self.assertRaises(ValueError, self.theclass.fromtimestamp,
952 insane)
953
Tim Peters2a799bf2002-12-16 20:18:38 +0000954 def test_today(self):
955 import time
956
957 # We claim that today() is like fromtimestamp(time.time()), so
958 # prove it.
959 for dummy in range(3):
960 today = self.theclass.today()
961 ts = time.time()
962 todayagain = self.theclass.fromtimestamp(ts)
963 if today == todayagain:
964 break
965 # There are several legit reasons that could fail:
966 # 1. It recently became midnight, between the today() and the
967 # time() calls.
968 # 2. The platform time() has such fine resolution that we'll
969 # never get the same value twice.
970 # 3. The platform time() has poor resolution, and we just
971 # happened to call today() right before a resolution quantum
972 # boundary.
973 # 4. The system clock got fiddled between calls.
974 # In any case, wait a little while and try again.
975 time.sleep(0.1)
976
977 # It worked or it didn't. If it didn't, assume it's reason #2, and
978 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000979 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000980 abs(todayagain - today) < timedelta(seconds=0.5))
981
982 def test_weekday(self):
983 for i in range(7):
984 # March 4, 2002 is a Monday
985 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
986 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
987 # January 2, 1956 is a Monday
988 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
989 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
990
991 def test_isocalendar(self):
992 # Check examples from
993 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
994 for i in range(7):
995 d = self.theclass(2003, 12, 22+i)
996 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
997 d = self.theclass(2003, 12, 29) + timedelta(i)
998 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
999 d = self.theclass(2004, 1, 5+i)
1000 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1001 d = self.theclass(2009, 12, 21+i)
1002 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1003 d = self.theclass(2009, 12, 28) + timedelta(i)
1004 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1005 d = self.theclass(2010, 1, 4+i)
1006 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1007
1008 def test_iso_long_years(self):
1009 # Calculate long ISO years and compare to table from
1010 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1011 ISO_LONG_YEARS_TABLE = """
1012 4 32 60 88
1013 9 37 65 93
1014 15 43 71 99
1015 20 48 76
1016 26 54 82
1017
1018 105 133 161 189
1019 111 139 167 195
1020 116 144 172
1021 122 150 178
1022 128 156 184
1023
1024 201 229 257 285
1025 207 235 263 291
1026 212 240 268 296
1027 218 246 274
1028 224 252 280
1029
1030 303 331 359 387
1031 308 336 364 392
1032 314 342 370 398
1033 320 348 376
1034 325 353 381
1035 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001036 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001037 L = []
1038 for i in range(400):
1039 d = self.theclass(2000+i, 12, 31)
1040 d1 = self.theclass(1600+i, 12, 31)
1041 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1042 if d.isocalendar()[1] == 53:
1043 L.append(i)
1044 self.assertEqual(L, iso_long_years)
1045
1046 def test_isoformat(self):
1047 t = self.theclass(2, 3, 2)
1048 self.assertEqual(t.isoformat(), "0002-03-02")
1049
1050 def test_ctime(self):
1051 t = self.theclass(2002, 3, 2)
1052 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1053
1054 def test_strftime(self):
1055 t = self.theclass(2005, 3, 2)
1056 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001057 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001058 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001059
1060 self.assertRaises(TypeError, t.strftime) # needs an arg
1061 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1062 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1063
Georg Brandlf78e02b2008-06-10 17:40:04 +00001064 # test that unicode input is allowed (issue 2782)
1065 self.assertEqual(t.strftime("%m"), "03")
1066
Tim Peters2a799bf2002-12-16 20:18:38 +00001067 # A naive object replaces %z and %Z w/ empty strings.
1068 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1069
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001070 #make sure that invalid format specifiers are handled correctly
1071 #self.assertRaises(ValueError, t.strftime, "%e")
1072 #self.assertRaises(ValueError, t.strftime, "%")
1073 #self.assertRaises(ValueError, t.strftime, "%#")
1074
1075 #oh well, some systems just ignore those invalid ones.
1076 #at least, excercise them to make sure that no crashes
1077 #are generated
1078 for f in ["%e", "%", "%#"]:
1079 try:
1080 t.strftime(f)
1081 except ValueError:
1082 pass
1083
1084 #check that this standard extension works
1085 t.strftime("%f")
1086
Georg Brandlf78e02b2008-06-10 17:40:04 +00001087
Eric Smith1ba31142007-09-11 18:06:02 +00001088 def test_format(self):
1089 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001090 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001091
1092 # check that a derived class's __str__() gets called
1093 class A(self.theclass):
1094 def __str__(self):
1095 return 'A'
1096 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001097 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001098
1099 # check that a derived class's strftime gets called
1100 class B(self.theclass):
1101 def strftime(self, format_spec):
1102 return 'B'
1103 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001104 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001105
1106 for fmt in ["m:%m d:%d y:%y",
1107 "m:%m d:%d y:%y H:%H M:%M S:%S",
1108 "%z %Z",
1109 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001110 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1111 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1112 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001113
Tim Peters2a799bf2002-12-16 20:18:38 +00001114 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001115 # XXX: Should min and max respect subclassing?
1116 if issubclass(self.theclass, datetime):
1117 expected_class = datetime
1118 else:
1119 expected_class = date
1120 self.assertIsInstance(self.theclass.min, expected_class)
1121 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001122 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001123 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001124
1125 def test_extreme_timedelta(self):
1126 big = self.theclass.max - self.theclass.min
1127 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1128 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1129 # n == 315537897599999999 ~= 2**58.13
1130 justasbig = timedelta(0, 0, n)
1131 self.assertEqual(big, justasbig)
1132 self.assertEqual(self.theclass.min + big, self.theclass.max)
1133 self.assertEqual(self.theclass.max - big, self.theclass.min)
1134
1135 def test_timetuple(self):
1136 for i in range(7):
1137 # January 2, 1956 is a Monday (0)
1138 d = self.theclass(1956, 1, 2+i)
1139 t = d.timetuple()
1140 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1141 # February 1, 1956 is a Wednesday (2)
1142 d = self.theclass(1956, 2, 1+i)
1143 t = d.timetuple()
1144 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1145 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1146 # of the year.
1147 d = self.theclass(1956, 3, 1+i)
1148 t = d.timetuple()
1149 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1150 self.assertEqual(t.tm_year, 1956)
1151 self.assertEqual(t.tm_mon, 3)
1152 self.assertEqual(t.tm_mday, 1+i)
1153 self.assertEqual(t.tm_hour, 0)
1154 self.assertEqual(t.tm_min, 0)
1155 self.assertEqual(t.tm_sec, 0)
1156 self.assertEqual(t.tm_wday, (3+i)%7)
1157 self.assertEqual(t.tm_yday, 61+i)
1158 self.assertEqual(t.tm_isdst, -1)
1159
1160 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001161 args = 6, 7, 23
1162 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001163 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001164 green = pickler.dumps(orig, proto)
1165 derived = unpickler.loads(green)
1166 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001167
1168 def test_compare(self):
1169 t1 = self.theclass(2, 3, 4)
1170 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001171 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001172 self.assertTrue(t1 <= t2)
1173 self.assertTrue(t1 >= t2)
1174 self.assertTrue(not t1 != t2)
1175 self.assertTrue(not t1 < t2)
1176 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001177
1178 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1179 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001180 self.assertTrue(t1 < t2)
1181 self.assertTrue(t2 > t1)
1182 self.assertTrue(t1 <= t2)
1183 self.assertTrue(t2 >= t1)
1184 self.assertTrue(t1 != t2)
1185 self.assertTrue(t2 != t1)
1186 self.assertTrue(not t1 == t2)
1187 self.assertTrue(not t2 == t1)
1188 self.assertTrue(not t1 > t2)
1189 self.assertTrue(not t2 < t1)
1190 self.assertTrue(not t1 >= t2)
1191 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001192
Tim Peters68124bb2003-02-08 03:46:31 +00001193 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001194 self.assertEqual(t1 == badarg, False)
1195 self.assertEqual(t1 != badarg, True)
1196 self.assertEqual(badarg == t1, False)
1197 self.assertEqual(badarg != t1, True)
1198
Tim Peters2a799bf2002-12-16 20:18:38 +00001199 self.assertRaises(TypeError, lambda: t1 < badarg)
1200 self.assertRaises(TypeError, lambda: t1 > badarg)
1201 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001202 self.assertRaises(TypeError, lambda: badarg <= t1)
1203 self.assertRaises(TypeError, lambda: badarg < t1)
1204 self.assertRaises(TypeError, lambda: badarg > t1)
1205 self.assertRaises(TypeError, lambda: badarg >= t1)
1206
Tim Peters8d81a012003-01-24 22:36:34 +00001207 def test_mixed_compare(self):
1208 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001209
1210 # Our class can be compared for equality to other classes
1211 self.assertEqual(our == 1, False)
1212 self.assertEqual(1 == our, False)
1213 self.assertEqual(our != 1, True)
1214 self.assertEqual(1 != our, True)
1215
1216 # But the ordering is undefined
1217 self.assertRaises(TypeError, lambda: our < 1)
1218 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001219
Guido van Rossum19960592006-08-24 17:29:38 +00001220 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001221
Guido van Rossum19960592006-08-24 17:29:38 +00001222 class SomeClass:
1223 pass
1224
1225 their = SomeClass()
1226 self.assertEqual(our == their, False)
1227 self.assertEqual(their == our, False)
1228 self.assertEqual(our != their, True)
1229 self.assertEqual(their != our, True)
1230 self.assertRaises(TypeError, lambda: our < their)
1231 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001232
Guido van Rossum19960592006-08-24 17:29:38 +00001233 # However, if the other class explicitly defines ordering
1234 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001235
Guido van Rossum19960592006-08-24 17:29:38 +00001236 class LargerThanAnything:
1237 def __lt__(self, other):
1238 return False
1239 def __le__(self, other):
1240 return isinstance(other, LargerThanAnything)
1241 def __eq__(self, other):
1242 return isinstance(other, LargerThanAnything)
1243 def __ne__(self, other):
1244 return not isinstance(other, LargerThanAnything)
1245 def __gt__(self, other):
1246 return not isinstance(other, LargerThanAnything)
1247 def __ge__(self, other):
1248 return True
1249
1250 their = LargerThanAnything()
1251 self.assertEqual(our == their, False)
1252 self.assertEqual(their == our, False)
1253 self.assertEqual(our != their, True)
1254 self.assertEqual(their != our, True)
1255 self.assertEqual(our < their, True)
1256 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001257
Tim Peters2a799bf2002-12-16 20:18:38 +00001258 def test_bool(self):
1259 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001260 self.assertTrue(self.theclass.min)
1261 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001262
Guido van Rossum04110fb2007-08-24 16:32:05 +00001263 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001264 # For nasty technical reasons, we can't handle years before 1900.
1265 cls = self.theclass
1266 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1267 for y in 1, 49, 51, 99, 100, 1000, 1899:
1268 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001269
1270 def test_replace(self):
1271 cls = self.theclass
1272 args = [1, 2, 3]
1273 base = cls(*args)
1274 self.assertEqual(base, base.replace())
1275
1276 i = 0
1277 for name, newval in (("year", 2),
1278 ("month", 3),
1279 ("day", 4)):
1280 newargs = args[:]
1281 newargs[i] = newval
1282 expected = cls(*newargs)
1283 got = base.replace(**{name: newval})
1284 self.assertEqual(expected, got)
1285 i += 1
1286
1287 # Out of bounds.
1288 base = cls(2000, 2, 29)
1289 self.assertRaises(ValueError, base.replace, year=2001)
1290
Tim Petersa98924a2003-05-17 05:55:19 +00001291 def test_subclass_date(self):
1292
1293 class C(self.theclass):
1294 theAnswer = 42
1295
1296 def __new__(cls, *args, **kws):
1297 temp = kws.copy()
1298 extra = temp.pop('extra')
1299 result = self.theclass.__new__(cls, *args, **temp)
1300 result.extra = extra
1301 return result
1302
1303 def newmeth(self, start):
1304 return start + self.year + self.month
1305
1306 args = 2003, 4, 14
1307
1308 dt1 = self.theclass(*args)
1309 dt2 = C(*args, **{'extra': 7})
1310
1311 self.assertEqual(dt2.__class__, C)
1312 self.assertEqual(dt2.theAnswer, 42)
1313 self.assertEqual(dt2.extra, 7)
1314 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1315 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1316
Tim Peters604c0132004-06-07 23:04:33 +00001317 def test_pickling_subclass_date(self):
1318
1319 args = 6, 7, 23
1320 orig = SubclassDate(*args)
1321 for pickler, unpickler, proto in pickle_choices:
1322 green = pickler.dumps(orig, proto)
1323 derived = unpickler.loads(green)
1324 self.assertEqual(orig, derived)
1325
Tim Peters3f606292004-03-21 23:38:41 +00001326 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001327 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001328 # This is a low-overhead backdoor. A user can (by intent or
1329 # mistake) pass a string directly, which (if it's the right length)
1330 # will get treated like a pickle, and bypass the normal sanity
1331 # checks in the constructor. This can create insane objects.
1332 # The constructor doesn't want to burn the time to validate all
1333 # fields, but does check the month field. This stops, e.g.,
1334 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001335 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001336 if not issubclass(self.theclass, datetime):
1337 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001338 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001339 self.assertRaises(TypeError, self.theclass,
1340 base[:2] + month_byte + base[3:])
1341 for ord_byte in range(1, 13):
1342 # This shouldn't blow up because of the month byte alone. If
1343 # the implementation changes to do more-careful checking, it may
1344 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001345 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001346
Tim Peters2a799bf2002-12-16 20:18:38 +00001347#############################################################################
1348# datetime tests
1349
Tim Peters604c0132004-06-07 23:04:33 +00001350class SubclassDatetime(datetime):
1351 sub_var = 1
1352
Tim Peters2a799bf2002-12-16 20:18:38 +00001353class TestDateTime(TestDate):
1354
1355 theclass = datetime
1356
1357 def test_basic_attributes(self):
1358 dt = self.theclass(2002, 3, 1, 12, 0)
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, 0)
1364 self.assertEqual(dt.second, 0)
1365 self.assertEqual(dt.microsecond, 0)
1366
1367 def test_basic_attributes_nonzero(self):
1368 # Make sure all attributes are non-zero so bugs in
1369 # bit-shifting access show up.
1370 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1371 self.assertEqual(dt.year, 2002)
1372 self.assertEqual(dt.month, 3)
1373 self.assertEqual(dt.day, 1)
1374 self.assertEqual(dt.hour, 12)
1375 self.assertEqual(dt.minute, 59)
1376 self.assertEqual(dt.second, 59)
1377 self.assertEqual(dt.microsecond, 8000)
1378
1379 def test_roundtrip(self):
1380 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1381 self.theclass.now()):
1382 # Verify dt -> string -> datetime identity.
1383 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001384 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001385 s = s[9:]
1386 dt2 = eval(s)
1387 self.assertEqual(dt, dt2)
1388
1389 # Verify identity via reconstructing from pieces.
1390 dt2 = self.theclass(dt.year, dt.month, dt.day,
1391 dt.hour, dt.minute, dt.second,
1392 dt.microsecond)
1393 self.assertEqual(dt, dt2)
1394
1395 def test_isoformat(self):
1396 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1397 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1398 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1399 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001400 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001401 # str is ISO format with the separator forced to a blank.
1402 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1403
1404 t = self.theclass(2, 3, 2)
1405 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1406 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1407 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1408 # str is ISO format with the separator forced to a blank.
1409 self.assertEqual(str(t), "0002-03-02 00:00:00")
1410
Eric Smith1ba31142007-09-11 18:06:02 +00001411 def test_format(self):
1412 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001413 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001414
1415 # check that a derived class's __str__() gets called
1416 class A(self.theclass):
1417 def __str__(self):
1418 return 'A'
1419 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001420 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001421
1422 # check that a derived class's strftime gets called
1423 class B(self.theclass):
1424 def strftime(self, format_spec):
1425 return 'B'
1426 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001427 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001428
1429 for fmt in ["m:%m d:%d y:%y",
1430 "m:%m d:%d y:%y H:%H M:%M S:%S",
1431 "%z %Z",
1432 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001433 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1434 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1435 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001436
Tim Peters2a799bf2002-12-16 20:18:38 +00001437 def test_more_ctime(self):
1438 # Test fields that TestDate doesn't touch.
1439 import time
1440
1441 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1442 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1443 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1444 # out. The difference is that t.ctime() produces " 2" for the day,
1445 # but platform ctime() produces "02" for the day. According to
1446 # C99, t.ctime() is correct here.
1447 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1448
1449 # So test a case where that difference doesn't matter.
1450 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1451 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1452
1453 def test_tz_independent_comparing(self):
1454 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1455 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1456 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1457 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001458 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001459
1460 # Make sure comparison doesn't forget microseconds, and isn't done
1461 # via comparing a float timestamp (an IEEE double doesn't have enough
1462 # precision to span microsecond resolution across years 1 thru 9999,
1463 # so comparing via timestamp necessarily calls some distinct values
1464 # equal).
1465 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1466 us = timedelta(microseconds=1)
1467 dt2 = dt1 + us
1468 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001469 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001470
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001471 def test_strftime_with_bad_tzname_replace(self):
1472 # verify ok if tzinfo.tzname().replace() returns a non-string
1473 class MyTzInfo(FixedOffset):
1474 def tzname(self, dt):
1475 class MyStr(str):
1476 def replace(self, *args):
1477 return None
1478 return MyStr('name')
1479 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1480 self.assertRaises(TypeError, t.strftime, '%Z')
1481
Tim Peters2a799bf2002-12-16 20:18:38 +00001482 def test_bad_constructor_arguments(self):
1483 # bad years
1484 self.theclass(MINYEAR, 1, 1) # no exception
1485 self.theclass(MAXYEAR, 1, 1) # no exception
1486 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1487 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1488 # bad months
1489 self.theclass(2000, 1, 1) # no exception
1490 self.theclass(2000, 12, 1) # no exception
1491 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1492 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1493 # bad days
1494 self.theclass(2000, 2, 29) # no exception
1495 self.theclass(2004, 2, 29) # no exception
1496 self.theclass(2400, 2, 29) # no exception
1497 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1498 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1499 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1500 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1501 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1502 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1503 # bad hours
1504 self.theclass(2000, 1, 31, 0) # no exception
1505 self.theclass(2000, 1, 31, 23) # no exception
1506 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1507 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1508 # bad minutes
1509 self.theclass(2000, 1, 31, 23, 0) # no exception
1510 self.theclass(2000, 1, 31, 23, 59) # no exception
1511 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1512 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1513 # bad seconds
1514 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1515 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1516 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1517 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1518 # bad microseconds
1519 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1520 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1521 self.assertRaises(ValueError, self.theclass,
1522 2000, 1, 31, 23, 59, 59, -1)
1523 self.assertRaises(ValueError, self.theclass,
1524 2000, 1, 31, 23, 59, 59,
1525 1000000)
1526
1527 def test_hash_equality(self):
1528 d = self.theclass(2000, 12, 31, 23, 30, 17)
1529 e = self.theclass(2000, 12, 31, 23, 30, 17)
1530 self.assertEqual(d, e)
1531 self.assertEqual(hash(d), hash(e))
1532
1533 dic = {d: 1}
1534 dic[e] = 2
1535 self.assertEqual(len(dic), 1)
1536 self.assertEqual(dic[d], 2)
1537 self.assertEqual(dic[e], 2)
1538
1539 d = self.theclass(2001, 1, 1, 0, 5, 17)
1540 e = self.theclass(2001, 1, 1, 0, 5, 17)
1541 self.assertEqual(d, e)
1542 self.assertEqual(hash(d), hash(e))
1543
1544 dic = {d: 1}
1545 dic[e] = 2
1546 self.assertEqual(len(dic), 1)
1547 self.assertEqual(dic[d], 2)
1548 self.assertEqual(dic[e], 2)
1549
1550 def test_computations(self):
1551 a = self.theclass(2002, 1, 31)
1552 b = self.theclass(1956, 1, 31)
1553 diff = a-b
1554 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1555 self.assertEqual(diff.seconds, 0)
1556 self.assertEqual(diff.microseconds, 0)
1557 a = self.theclass(2002, 3, 2, 17, 6)
1558 millisec = timedelta(0, 0, 1000)
1559 hour = timedelta(0, 3600)
1560 day = timedelta(1)
1561 week = timedelta(7)
1562 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1563 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1564 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1565 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1566 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1567 self.assertEqual(a - hour, a + -hour)
1568 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1569 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1570 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1571 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1572 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1573 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1574 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1575 self.assertEqual((a + week) - a, week)
1576 self.assertEqual((a + day) - a, day)
1577 self.assertEqual((a + hour) - a, hour)
1578 self.assertEqual((a + millisec) - a, millisec)
1579 self.assertEqual((a - week) - a, -week)
1580 self.assertEqual((a - day) - a, -day)
1581 self.assertEqual((a - hour) - a, -hour)
1582 self.assertEqual((a - millisec) - a, -millisec)
1583 self.assertEqual(a - (a + week), -week)
1584 self.assertEqual(a - (a + day), -day)
1585 self.assertEqual(a - (a + hour), -hour)
1586 self.assertEqual(a - (a + millisec), -millisec)
1587 self.assertEqual(a - (a - week), week)
1588 self.assertEqual(a - (a - day), day)
1589 self.assertEqual(a - (a - hour), hour)
1590 self.assertEqual(a - (a - millisec), millisec)
1591 self.assertEqual(a + (week + day + hour + millisec),
1592 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1593 self.assertEqual(a + (week + day + hour + millisec),
1594 (((a + week) + day) + hour) + millisec)
1595 self.assertEqual(a - (week + day + hour + millisec),
1596 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1597 self.assertEqual(a - (week + day + hour + millisec),
1598 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001599 # Add/sub ints or floats should be illegal
1600 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001601 self.assertRaises(TypeError, lambda: a+i)
1602 self.assertRaises(TypeError, lambda: a-i)
1603 self.assertRaises(TypeError, lambda: i+a)
1604 self.assertRaises(TypeError, lambda: i-a)
1605
1606 # delta - datetime is senseless.
1607 self.assertRaises(TypeError, lambda: day - a)
1608 # mixing datetime and (delta or datetime) via * or // is senseless
1609 self.assertRaises(TypeError, lambda: day * a)
1610 self.assertRaises(TypeError, lambda: a * day)
1611 self.assertRaises(TypeError, lambda: day // a)
1612 self.assertRaises(TypeError, lambda: a // day)
1613 self.assertRaises(TypeError, lambda: a * a)
1614 self.assertRaises(TypeError, lambda: a // a)
1615 # datetime + datetime is senseless
1616 self.assertRaises(TypeError, lambda: a + a)
1617
1618 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001619 args = 6, 7, 23, 20, 59, 1, 64**2
1620 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001621 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001622 green = pickler.dumps(orig, proto)
1623 derived = unpickler.loads(green)
1624 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001625
Guido van Rossum275666f2003-02-07 21:49:01 +00001626 def test_more_pickling(self):
1627 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1628 s = pickle.dumps(a)
1629 b = pickle.loads(s)
1630 self.assertEqual(b.year, 2003)
1631 self.assertEqual(b.month, 2)
1632 self.assertEqual(b.day, 7)
1633
Tim Peters604c0132004-06-07 23:04:33 +00001634 def test_pickling_subclass_datetime(self):
1635 args = 6, 7, 23, 20, 59, 1, 64**2
1636 orig = SubclassDatetime(*args)
1637 for pickler, unpickler, proto in pickle_choices:
1638 green = pickler.dumps(orig, proto)
1639 derived = unpickler.loads(green)
1640 self.assertEqual(orig, derived)
1641
Tim Peters2a799bf2002-12-16 20:18:38 +00001642 def test_more_compare(self):
1643 # The test_compare() inherited from TestDate covers the error cases.
1644 # We just want to test lexicographic ordering on the members datetime
1645 # has that date lacks.
1646 args = [2000, 11, 29, 20, 58, 16, 999998]
1647 t1 = self.theclass(*args)
1648 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001649 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001650 self.assertTrue(t1 <= t2)
1651 self.assertTrue(t1 >= t2)
1652 self.assertTrue(not t1 != t2)
1653 self.assertTrue(not t1 < t2)
1654 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001655
1656 for i in range(len(args)):
1657 newargs = args[:]
1658 newargs[i] = args[i] + 1
1659 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001660 self.assertTrue(t1 < t2)
1661 self.assertTrue(t2 > t1)
1662 self.assertTrue(t1 <= t2)
1663 self.assertTrue(t2 >= t1)
1664 self.assertTrue(t1 != t2)
1665 self.assertTrue(t2 != t1)
1666 self.assertTrue(not t1 == t2)
1667 self.assertTrue(not t2 == t1)
1668 self.assertTrue(not t1 > t2)
1669 self.assertTrue(not t2 < t1)
1670 self.assertTrue(not t1 >= t2)
1671 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001672
1673
1674 # A helper for timestamp constructor tests.
1675 def verify_field_equality(self, expected, got):
1676 self.assertEqual(expected.tm_year, got.year)
1677 self.assertEqual(expected.tm_mon, got.month)
1678 self.assertEqual(expected.tm_mday, got.day)
1679 self.assertEqual(expected.tm_hour, got.hour)
1680 self.assertEqual(expected.tm_min, got.minute)
1681 self.assertEqual(expected.tm_sec, got.second)
1682
1683 def test_fromtimestamp(self):
1684 import time
1685
1686 ts = time.time()
1687 expected = time.localtime(ts)
1688 got = self.theclass.fromtimestamp(ts)
1689 self.verify_field_equality(expected, got)
1690
1691 def test_utcfromtimestamp(self):
1692 import time
1693
1694 ts = time.time()
1695 expected = time.gmtime(ts)
1696 got = self.theclass.utcfromtimestamp(ts)
1697 self.verify_field_equality(expected, got)
1698
Thomas Wouters477c8d52006-05-27 19:21:47 +00001699 def test_microsecond_rounding(self):
1700 # Test whether fromtimestamp "rounds up" floats that are less
1701 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001702 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1703 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001704
Tim Peters1b6f7a92004-06-20 02:50:16 +00001705 def test_insane_fromtimestamp(self):
1706 # It's possible that some platform maps time_t to double,
1707 # and that this test will fail there. This test should
1708 # exempt such platforms (provided they return reasonable
1709 # results!).
1710 for insane in -1e200, 1e200:
1711 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1712 insane)
1713
1714 def test_insane_utcfromtimestamp(self):
1715 # It's possible that some platform maps time_t to double,
1716 # and that this test will fail there. This test should
1717 # exempt such platforms (provided they return reasonable
1718 # results!).
1719 for insane in -1e200, 1e200:
1720 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1721 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001722 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001723 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001724 # The result is tz-dependent; at least test that this doesn't
1725 # fail (like it did before bug 1646728 was fixed).
1726 self.theclass.fromtimestamp(-1.05)
1727
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001728 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001729 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001730 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001731 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001732
Tim Peters2a799bf2002-12-16 20:18:38 +00001733 def test_utcnow(self):
1734 import time
1735
1736 # Call it a success if utcnow() and utcfromtimestamp() are within
1737 # a second of each other.
1738 tolerance = timedelta(seconds=1)
1739 for dummy in range(3):
1740 from_now = self.theclass.utcnow()
1741 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1742 if abs(from_timestamp - from_now) <= tolerance:
1743 break
1744 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001745 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001746
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001747 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001748 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001749
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001750 string = '2004-12-01 13:02:47.197'
1751 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001752 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001753 got = self.theclass.strptime(string, format)
1754 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001755 self.assertIs(type(expected), self.theclass)
1756 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001757
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001758 strptime = self.theclass.strptime
1759 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1760 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1761 # Only local timezone and UTC are supported
1762 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1763 (-_time.timezone, _time.tzname[0])):
1764 if tzseconds < 0:
1765 sign = '-'
1766 seconds = -tzseconds
1767 else:
1768 sign ='+'
1769 seconds = tzseconds
1770 hours, minutes = divmod(seconds//60, 60)
1771 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1772 dt = strptime(dtstr, "%z %Z")
1773 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1774 self.assertEqual(dt.tzname(), tzname)
1775 # Can produce inconsistent datetime
1776 dtstr, fmt = "+1234 UTC", "%z %Z"
1777 dt = strptime(dtstr, fmt)
1778 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1779 self.assertEqual(dt.tzname(), 'UTC')
1780 # yet will roundtrip
1781 self.assertEqual(dt.strftime(fmt), dtstr)
1782
1783 # Produce naive datetime if no %z is provided
1784 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1785
1786 with self.assertRaises(ValueError): strptime("-2400", "%z")
1787 with self.assertRaises(ValueError): strptime("-000", "%z")
1788
Tim Peters2a799bf2002-12-16 20:18:38 +00001789 def test_more_timetuple(self):
1790 # This tests fields beyond those tested by the TestDate.test_timetuple.
1791 t = self.theclass(2004, 12, 31, 6, 22, 33)
1792 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1793 self.assertEqual(t.timetuple(),
1794 (t.year, t.month, t.day,
1795 t.hour, t.minute, t.second,
1796 t.weekday(),
1797 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1798 -1))
1799 tt = t.timetuple()
1800 self.assertEqual(tt.tm_year, t.year)
1801 self.assertEqual(tt.tm_mon, t.month)
1802 self.assertEqual(tt.tm_mday, t.day)
1803 self.assertEqual(tt.tm_hour, t.hour)
1804 self.assertEqual(tt.tm_min, t.minute)
1805 self.assertEqual(tt.tm_sec, t.second)
1806 self.assertEqual(tt.tm_wday, t.weekday())
1807 self.assertEqual(tt.tm_yday, t.toordinal() -
1808 date(t.year, 1, 1).toordinal() + 1)
1809 self.assertEqual(tt.tm_isdst, -1)
1810
1811 def test_more_strftime(self):
1812 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001813 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1814 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1815 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001816
1817 def test_extract(self):
1818 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1819 self.assertEqual(dt.date(), date(2002, 3, 4))
1820 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1821
1822 def test_combine(self):
1823 d = date(2002, 3, 4)
1824 t = time(18, 45, 3, 1234)
1825 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1826 combine = self.theclass.combine
1827 dt = combine(d, t)
1828 self.assertEqual(dt, expected)
1829
1830 dt = combine(time=t, date=d)
1831 self.assertEqual(dt, expected)
1832
1833 self.assertEqual(d, dt.date())
1834 self.assertEqual(t, dt.time())
1835 self.assertEqual(dt, combine(dt.date(), dt.time()))
1836
1837 self.assertRaises(TypeError, combine) # need an arg
1838 self.assertRaises(TypeError, combine, d) # need two args
1839 self.assertRaises(TypeError, combine, t, d) # args reversed
1840 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1841 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1842
Tim Peters12bf3392002-12-24 05:41:27 +00001843 def test_replace(self):
1844 cls = self.theclass
1845 args = [1, 2, 3, 4, 5, 6, 7]
1846 base = cls(*args)
1847 self.assertEqual(base, base.replace())
1848
1849 i = 0
1850 for name, newval in (("year", 2),
1851 ("month", 3),
1852 ("day", 4),
1853 ("hour", 5),
1854 ("minute", 6),
1855 ("second", 7),
1856 ("microsecond", 8)):
1857 newargs = args[:]
1858 newargs[i] = newval
1859 expected = cls(*newargs)
1860 got = base.replace(**{name: newval})
1861 self.assertEqual(expected, got)
1862 i += 1
1863
1864 # Out of bounds.
1865 base = cls(2000, 2, 29)
1866 self.assertRaises(ValueError, base.replace, year=2001)
1867
Tim Peters80475bb2002-12-25 07:40:55 +00001868 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001869 # Pretty boring! The TZ test is more interesting here. astimezone()
1870 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001871 dt = self.theclass.now()
1872 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001873 self.assertRaises(TypeError, dt.astimezone) # not enough args
1874 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1875 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001876 self.assertRaises(ValueError, dt.astimezone, f) # naive
1877 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001878
Tim Peters52dcce22003-01-23 16:36:11 +00001879 class Bogus(tzinfo):
1880 def utcoffset(self, dt): return None
1881 def dst(self, dt): return timedelta(0)
1882 bog = Bogus()
1883 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1884
1885 class AlsoBogus(tzinfo):
1886 def utcoffset(self, dt): return timedelta(0)
1887 def dst(self, dt): return None
1888 alsobog = AlsoBogus()
1889 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001890
Tim Petersa98924a2003-05-17 05:55:19 +00001891 def test_subclass_datetime(self):
1892
1893 class C(self.theclass):
1894 theAnswer = 42
1895
1896 def __new__(cls, *args, **kws):
1897 temp = kws.copy()
1898 extra = temp.pop('extra')
1899 result = self.theclass.__new__(cls, *args, **temp)
1900 result.extra = extra
1901 return result
1902
1903 def newmeth(self, start):
1904 return start + self.year + self.month + self.second
1905
1906 args = 2003, 4, 14, 12, 13, 41
1907
1908 dt1 = self.theclass(*args)
1909 dt2 = C(*args, **{'extra': 7})
1910
1911 self.assertEqual(dt2.__class__, C)
1912 self.assertEqual(dt2.theAnswer, 42)
1913 self.assertEqual(dt2.extra, 7)
1914 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1915 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1916 dt1.second - 7)
1917
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001918class TestSubclassDateTime(TestDateTime):
1919 theclass = SubclassDatetime
1920 # Override tests not designed for subclass
1921 def test_roundtrip(self):
1922 pass
1923
Tim Peters604c0132004-06-07 23:04:33 +00001924class SubclassTime(time):
1925 sub_var = 1
1926
Guido van Rossumd8faa362007-04-27 19:54:29 +00001927class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001928
1929 theclass = time
1930
1931 def test_basic_attributes(self):
1932 t = self.theclass(12, 0)
1933 self.assertEqual(t.hour, 12)
1934 self.assertEqual(t.minute, 0)
1935 self.assertEqual(t.second, 0)
1936 self.assertEqual(t.microsecond, 0)
1937
1938 def test_basic_attributes_nonzero(self):
1939 # Make sure all attributes are non-zero so bugs in
1940 # bit-shifting access show up.
1941 t = self.theclass(12, 59, 59, 8000)
1942 self.assertEqual(t.hour, 12)
1943 self.assertEqual(t.minute, 59)
1944 self.assertEqual(t.second, 59)
1945 self.assertEqual(t.microsecond, 8000)
1946
1947 def test_roundtrip(self):
1948 t = self.theclass(1, 2, 3, 4)
1949
1950 # Verify t -> string -> time identity.
1951 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001952 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001953 s = s[9:]
1954 t2 = eval(s)
1955 self.assertEqual(t, t2)
1956
1957 # Verify identity via reconstructing from pieces.
1958 t2 = self.theclass(t.hour, t.minute, t.second,
1959 t.microsecond)
1960 self.assertEqual(t, t2)
1961
1962 def test_comparing(self):
1963 args = [1, 2, 3, 4]
1964 t1 = self.theclass(*args)
1965 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001966 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001967 self.assertTrue(t1 <= t2)
1968 self.assertTrue(t1 >= t2)
1969 self.assertTrue(not t1 != t2)
1970 self.assertTrue(not t1 < t2)
1971 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001972
1973 for i in range(len(args)):
1974 newargs = args[:]
1975 newargs[i] = args[i] + 1
1976 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001977 self.assertTrue(t1 < t2)
1978 self.assertTrue(t2 > t1)
1979 self.assertTrue(t1 <= t2)
1980 self.assertTrue(t2 >= t1)
1981 self.assertTrue(t1 != t2)
1982 self.assertTrue(t2 != t1)
1983 self.assertTrue(not t1 == t2)
1984 self.assertTrue(not t2 == t1)
1985 self.assertTrue(not t1 > t2)
1986 self.assertTrue(not t2 < t1)
1987 self.assertTrue(not t1 >= t2)
1988 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001989
Tim Peters68124bb2003-02-08 03:46:31 +00001990 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001991 self.assertEqual(t1 == badarg, False)
1992 self.assertEqual(t1 != badarg, True)
1993 self.assertEqual(badarg == t1, False)
1994 self.assertEqual(badarg != t1, True)
1995
Tim Peters2a799bf2002-12-16 20:18:38 +00001996 self.assertRaises(TypeError, lambda: t1 <= badarg)
1997 self.assertRaises(TypeError, lambda: t1 < badarg)
1998 self.assertRaises(TypeError, lambda: t1 > badarg)
1999 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00002000 self.assertRaises(TypeError, lambda: badarg <= t1)
2001 self.assertRaises(TypeError, lambda: badarg < t1)
2002 self.assertRaises(TypeError, lambda: badarg > t1)
2003 self.assertRaises(TypeError, lambda: badarg >= t1)
2004
2005 def test_bad_constructor_arguments(self):
2006 # bad hours
2007 self.theclass(0, 0) # no exception
2008 self.theclass(23, 0) # no exception
2009 self.assertRaises(ValueError, self.theclass, -1, 0)
2010 self.assertRaises(ValueError, self.theclass, 24, 0)
2011 # bad minutes
2012 self.theclass(23, 0) # no exception
2013 self.theclass(23, 59) # no exception
2014 self.assertRaises(ValueError, self.theclass, 23, -1)
2015 self.assertRaises(ValueError, self.theclass, 23, 60)
2016 # bad seconds
2017 self.theclass(23, 59, 0) # no exception
2018 self.theclass(23, 59, 59) # no exception
2019 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2020 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2021 # bad microseconds
2022 self.theclass(23, 59, 59, 0) # no exception
2023 self.theclass(23, 59, 59, 999999) # no exception
2024 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2025 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2026
2027 def test_hash_equality(self):
2028 d = self.theclass(23, 30, 17)
2029 e = self.theclass(23, 30, 17)
2030 self.assertEqual(d, e)
2031 self.assertEqual(hash(d), hash(e))
2032
2033 dic = {d: 1}
2034 dic[e] = 2
2035 self.assertEqual(len(dic), 1)
2036 self.assertEqual(dic[d], 2)
2037 self.assertEqual(dic[e], 2)
2038
2039 d = self.theclass(0, 5, 17)
2040 e = self.theclass(0, 5, 17)
2041 self.assertEqual(d, e)
2042 self.assertEqual(hash(d), hash(e))
2043
2044 dic = {d: 1}
2045 dic[e] = 2
2046 self.assertEqual(len(dic), 1)
2047 self.assertEqual(dic[d], 2)
2048 self.assertEqual(dic[e], 2)
2049
2050 def test_isoformat(self):
2051 t = self.theclass(4, 5, 1, 123)
2052 self.assertEqual(t.isoformat(), "04:05:01.000123")
2053 self.assertEqual(t.isoformat(), str(t))
2054
2055 t = self.theclass()
2056 self.assertEqual(t.isoformat(), "00:00:00")
2057 self.assertEqual(t.isoformat(), str(t))
2058
2059 t = self.theclass(microsecond=1)
2060 self.assertEqual(t.isoformat(), "00:00:00.000001")
2061 self.assertEqual(t.isoformat(), str(t))
2062
2063 t = self.theclass(microsecond=10)
2064 self.assertEqual(t.isoformat(), "00:00:00.000010")
2065 self.assertEqual(t.isoformat(), str(t))
2066
2067 t = self.theclass(microsecond=100)
2068 self.assertEqual(t.isoformat(), "00:00:00.000100")
2069 self.assertEqual(t.isoformat(), str(t))
2070
2071 t = self.theclass(microsecond=1000)
2072 self.assertEqual(t.isoformat(), "00:00:00.001000")
2073 self.assertEqual(t.isoformat(), str(t))
2074
2075 t = self.theclass(microsecond=10000)
2076 self.assertEqual(t.isoformat(), "00:00:00.010000")
2077 self.assertEqual(t.isoformat(), str(t))
2078
2079 t = self.theclass(microsecond=100000)
2080 self.assertEqual(t.isoformat(), "00:00:00.100000")
2081 self.assertEqual(t.isoformat(), str(t))
2082
Thomas Wouterscf297e42007-02-23 15:07:44 +00002083 def test_1653736(self):
2084 # verify it doesn't accept extra keyword arguments
2085 t = self.theclass(second=1)
2086 self.assertRaises(TypeError, t.isoformat, foo=3)
2087
Tim Peters2a799bf2002-12-16 20:18:38 +00002088 def test_strftime(self):
2089 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002090 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002091 # A naive object replaces %z and %Z with empty strings.
2092 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2093
Eric Smith1ba31142007-09-11 18:06:02 +00002094 def test_format(self):
2095 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002096 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002097
2098 # check that a derived class's __str__() gets called
2099 class A(self.theclass):
2100 def __str__(self):
2101 return 'A'
2102 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002103 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002104
2105 # check that a derived class's strftime gets called
2106 class B(self.theclass):
2107 def strftime(self, format_spec):
2108 return 'B'
2109 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002110 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002111
2112 for fmt in ['%H %M %S',
2113 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002114 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2115 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2116 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002117
Tim Peters2a799bf2002-12-16 20:18:38 +00002118 def test_str(self):
2119 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2120 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2121 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2122 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2123 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2124
2125 def test_repr(self):
2126 name = 'datetime.' + self.theclass.__name__
2127 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2128 "%s(1, 2, 3, 4)" % name)
2129 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2130 "%s(10, 2, 3, 4000)" % name)
2131 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2132 "%s(0, 2, 3, 400000)" % name)
2133 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2134 "%s(12, 2, 3)" % name)
2135 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2136 "%s(23, 15)" % name)
2137
2138 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002139 self.assertIsInstance(self.theclass.min, self.theclass)
2140 self.assertIsInstance(self.theclass.max, self.theclass)
2141 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002142 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002143
2144 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002145 args = 20, 59, 16, 64**2
2146 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002147 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002148 green = pickler.dumps(orig, proto)
2149 derived = unpickler.loads(green)
2150 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002151
Tim Peters604c0132004-06-07 23:04:33 +00002152 def test_pickling_subclass_time(self):
2153 args = 20, 59, 16, 64**2
2154 orig = SubclassTime(*args)
2155 for pickler, unpickler, proto in pickle_choices:
2156 green = pickler.dumps(orig, proto)
2157 derived = unpickler.loads(green)
2158 self.assertEqual(orig, derived)
2159
Tim Peters2a799bf2002-12-16 20:18:38 +00002160 def test_bool(self):
2161 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002162 self.assertTrue(cls(1))
2163 self.assertTrue(cls(0, 1))
2164 self.assertTrue(cls(0, 0, 1))
2165 self.assertTrue(cls(0, 0, 0, 1))
2166 self.assertTrue(not cls(0))
2167 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002168
Tim Peters12bf3392002-12-24 05:41:27 +00002169 def test_replace(self):
2170 cls = self.theclass
2171 args = [1, 2, 3, 4]
2172 base = cls(*args)
2173 self.assertEqual(base, base.replace())
2174
2175 i = 0
2176 for name, newval in (("hour", 5),
2177 ("minute", 6),
2178 ("second", 7),
2179 ("microsecond", 8)):
2180 newargs = args[:]
2181 newargs[i] = newval
2182 expected = cls(*newargs)
2183 got = base.replace(**{name: newval})
2184 self.assertEqual(expected, got)
2185 i += 1
2186
2187 # Out of bounds.
2188 base = cls(1)
2189 self.assertRaises(ValueError, base.replace, hour=24)
2190 self.assertRaises(ValueError, base.replace, minute=-1)
2191 self.assertRaises(ValueError, base.replace, second=100)
2192 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2193
Tim Petersa98924a2003-05-17 05:55:19 +00002194 def test_subclass_time(self):
2195
2196 class C(self.theclass):
2197 theAnswer = 42
2198
2199 def __new__(cls, *args, **kws):
2200 temp = kws.copy()
2201 extra = temp.pop('extra')
2202 result = self.theclass.__new__(cls, *args, **temp)
2203 result.extra = extra
2204 return result
2205
2206 def newmeth(self, start):
2207 return start + self.hour + self.second
2208
2209 args = 4, 5, 6
2210
2211 dt1 = self.theclass(*args)
2212 dt2 = C(*args, **{'extra': 7})
2213
2214 self.assertEqual(dt2.__class__, C)
2215 self.assertEqual(dt2.theAnswer, 42)
2216 self.assertEqual(dt2.extra, 7)
2217 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2218 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2219
Armin Rigof4afb212005-11-07 07:15:48 +00002220 def test_backdoor_resistance(self):
2221 # see TestDate.test_backdoor_resistance().
2222 base = '2:59.0'
2223 for hour_byte in ' ', '9', chr(24), '\xff':
2224 self.assertRaises(TypeError, self.theclass,
2225 hour_byte + base[1:])
2226
Tim Peters855fe882002-12-22 03:43:39 +00002227# A mixin for classes with a tzinfo= argument. Subclasses must define
2228# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002229# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002230class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002231
Tim Petersbad8ff02002-12-30 20:52:32 +00002232 def test_argument_passing(self):
2233 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002234 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002235 class introspective(tzinfo):
2236 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002237 def utcoffset(self, dt):
2238 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002239 dst = utcoffset
2240
2241 obj = cls(1, 2, 3, tzinfo=introspective())
2242
Tim Peters0bf60bd2003-01-08 20:40:01 +00002243 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002244 self.assertEqual(obj.tzname(), expected)
2245
Tim Peters0bf60bd2003-01-08 20:40:01 +00002246 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002247 self.assertEqual(obj.utcoffset(), expected)
2248 self.assertEqual(obj.dst(), expected)
2249
Tim Peters855fe882002-12-22 03:43:39 +00002250 def test_bad_tzinfo_classes(self):
2251 cls = self.theclass
2252 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002253
Tim Peters855fe882002-12-22 03:43:39 +00002254 class NiceTry(object):
2255 def __init__(self): pass
2256 def utcoffset(self, dt): pass
2257 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2258
2259 class BetterTry(tzinfo):
2260 def __init__(self): pass
2261 def utcoffset(self, dt): pass
2262 b = BetterTry()
2263 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002264 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002265
2266 def test_utc_offset_out_of_bounds(self):
2267 class Edgy(tzinfo):
2268 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002269 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002270 def utcoffset(self, dt):
2271 return self.offset
2272
2273 cls = self.theclass
2274 for offset, legit in ((-1440, False),
2275 (-1439, True),
2276 (1439, True),
2277 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002278 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002279 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002280 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002281 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002282 else:
2283 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002284 if legit:
2285 aofs = abs(offset)
2286 h, m = divmod(aofs, 60)
2287 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002288 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002289 t = t.timetz()
2290 self.assertEqual(str(t), "01:02:03" + tag)
2291 else:
2292 self.assertRaises(ValueError, str, t)
2293
2294 def test_tzinfo_classes(self):
2295 cls = self.theclass
2296 class C1(tzinfo):
2297 def utcoffset(self, dt): return None
2298 def dst(self, dt): return None
2299 def tzname(self, dt): return None
2300 for t in (cls(1, 1, 1),
2301 cls(1, 1, 1, tzinfo=None),
2302 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002303 self.assertTrue(t.utcoffset() is None)
2304 self.assertTrue(t.dst() is None)
2305 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002306
Tim Peters855fe882002-12-22 03:43:39 +00002307 class C3(tzinfo):
2308 def utcoffset(self, dt): return timedelta(minutes=-1439)
2309 def dst(self, dt): return timedelta(minutes=1439)
2310 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002311 t = cls(1, 1, 1, tzinfo=C3())
2312 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2313 self.assertEqual(t.dst(), timedelta(minutes=1439))
2314 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002315
2316 # Wrong types.
2317 class C4(tzinfo):
2318 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002319 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002320 def tzname(self, dt): return 0
2321 t = cls(1, 1, 1, tzinfo=C4())
2322 self.assertRaises(TypeError, t.utcoffset)
2323 self.assertRaises(TypeError, t.dst)
2324 self.assertRaises(TypeError, t.tzname)
2325
2326 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002327 class C6(tzinfo):
2328 def utcoffset(self, dt): return timedelta(hours=-24)
2329 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002330 t = cls(1, 1, 1, tzinfo=C6())
2331 self.assertRaises(ValueError, t.utcoffset)
2332 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002333
2334 # Not a whole number of minutes.
2335 class C7(tzinfo):
2336 def utcoffset(self, dt): return timedelta(seconds=61)
2337 def dst(self, dt): return timedelta(microseconds=-81)
2338 t = cls(1, 1, 1, tzinfo=C7())
2339 self.assertRaises(ValueError, t.utcoffset)
2340 self.assertRaises(ValueError, t.dst)
2341
Tim Peters4c0db782002-12-26 05:01:19 +00002342 def test_aware_compare(self):
2343 cls = self.theclass
2344
Tim Peters60c76e42002-12-27 00:41:11 +00002345 # Ensure that utcoffset() gets ignored if the comparands have
2346 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002347 class OperandDependentOffset(tzinfo):
2348 def utcoffset(self, t):
2349 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002350 # d0 and d1 equal after adjustment
2351 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002352 else:
Tim Peters397301e2003-01-02 21:28:08 +00002353 # d2 off in the weeds
2354 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002355
2356 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2357 d0 = base.replace(minute=3)
2358 d1 = base.replace(minute=9)
2359 d2 = base.replace(minute=11)
2360 for x in d0, d1, d2:
2361 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002362 for op in lt, le, gt, ge, eq, ne:
2363 got = op(x, y)
2364 expected = op(x.minute, y.minute)
2365 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002366
2367 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002368 # Note that a time can't actually have an operand-depedent offset,
2369 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2370 # so skip this test for time.
2371 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002372 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2373 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2374 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2375 for x in d0, d1, d2:
2376 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002377 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002378 if (x is d0 or x is d1) and (y is d0 or y is d1):
2379 expected = 0
2380 elif x is y is d2:
2381 expected = 0
2382 elif x is d2:
2383 expected = -1
2384 else:
2385 assert y is d2
2386 expected = 1
2387 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002388
Tim Peters855fe882002-12-22 03:43:39 +00002389
Tim Peters0bf60bd2003-01-08 20:40:01 +00002390# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002391class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002392 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002393
2394 def test_empty(self):
2395 t = self.theclass()
2396 self.assertEqual(t.hour, 0)
2397 self.assertEqual(t.minute, 0)
2398 self.assertEqual(t.second, 0)
2399 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002400 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002401
Tim Peters2a799bf2002-12-16 20:18:38 +00002402 def test_zones(self):
2403 est = FixedOffset(-300, "EST", 1)
2404 utc = FixedOffset(0, "UTC", -2)
2405 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002406 t1 = time( 7, 47, tzinfo=est)
2407 t2 = time(12, 47, tzinfo=utc)
2408 t3 = time(13, 47, tzinfo=met)
2409 t4 = time(microsecond=40)
2410 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002411
2412 self.assertEqual(t1.tzinfo, est)
2413 self.assertEqual(t2.tzinfo, utc)
2414 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002415 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002416 self.assertEqual(t5.tzinfo, utc)
2417
Tim Peters855fe882002-12-22 03:43:39 +00002418 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2419 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2420 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002421 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002422 self.assertRaises(TypeError, t1.utcoffset, "no args")
2423
2424 self.assertEqual(t1.tzname(), "EST")
2425 self.assertEqual(t2.tzname(), "UTC")
2426 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002427 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002428 self.assertRaises(TypeError, t1.tzname, "no args")
2429
Tim Peters855fe882002-12-22 03:43:39 +00002430 self.assertEqual(t1.dst(), timedelta(minutes=1))
2431 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2432 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002433 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002434 self.assertRaises(TypeError, t1.dst, "no args")
2435
2436 self.assertEqual(hash(t1), hash(t2))
2437 self.assertEqual(hash(t1), hash(t3))
2438 self.assertEqual(hash(t2), hash(t3))
2439
2440 self.assertEqual(t1, t2)
2441 self.assertEqual(t1, t3)
2442 self.assertEqual(t2, t3)
2443 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2444 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2445 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2446
2447 self.assertEqual(str(t1), "07:47:00-05:00")
2448 self.assertEqual(str(t2), "12:47:00+00:00")
2449 self.assertEqual(str(t3), "13:47:00+01:00")
2450 self.assertEqual(str(t4), "00:00:00.000040")
2451 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2452
2453 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2454 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2455 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2456 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2457 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2458
Tim Peters0bf60bd2003-01-08 20:40:01 +00002459 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002460 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2461 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2462 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2463 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2464 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2465
2466 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2467 "07:47:00 %Z=EST %z=-0500")
2468 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2469 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2470
2471 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002472 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002473 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2474 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2475
Tim Petersb92bb712002-12-21 17:44:07 +00002476 # Check that an invalid tzname result raises an exception.
2477 class Badtzname(tzinfo):
2478 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002479 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002480 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2481 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002482
2483 def test_hash_edge_cases(self):
2484 # Offsets that overflow a basic time.
2485 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2486 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2487 self.assertEqual(hash(t1), hash(t2))
2488
2489 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2490 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2491 self.assertEqual(hash(t1), hash(t2))
2492
Tim Peters2a799bf2002-12-16 20:18:38 +00002493 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002494 # Try one without a tzinfo.
2495 args = 20, 59, 16, 64**2
2496 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002497 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002498 green = pickler.dumps(orig, proto)
2499 derived = unpickler.loads(green)
2500 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002501
2502 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002503 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002504 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002505 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002506 green = pickler.dumps(orig, proto)
2507 derived = unpickler.loads(green)
2508 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002509 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002510 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2511 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002512
2513 def test_more_bool(self):
2514 # Test cases with non-None tzinfo.
2515 cls = self.theclass
2516
2517 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002518 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002519
2520 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002521 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002522
2523 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002524 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002525
2526 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002527 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002528
2529 # Mostly ensuring this doesn't overflow internally.
2530 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002531 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002532
2533 # But this should yield a value error -- the utcoffset is bogus.
2534 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2535 self.assertRaises(ValueError, lambda: bool(t))
2536
2537 # Likewise.
2538 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2539 self.assertRaises(ValueError, lambda: bool(t))
2540
Tim Peters12bf3392002-12-24 05:41:27 +00002541 def test_replace(self):
2542 cls = self.theclass
2543 z100 = FixedOffset(100, "+100")
2544 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2545 args = [1, 2, 3, 4, z100]
2546 base = cls(*args)
2547 self.assertEqual(base, base.replace())
2548
2549 i = 0
2550 for name, newval in (("hour", 5),
2551 ("minute", 6),
2552 ("second", 7),
2553 ("microsecond", 8),
2554 ("tzinfo", zm200)):
2555 newargs = args[:]
2556 newargs[i] = newval
2557 expected = cls(*newargs)
2558 got = base.replace(**{name: newval})
2559 self.assertEqual(expected, got)
2560 i += 1
2561
2562 # Ensure we can get rid of a tzinfo.
2563 self.assertEqual(base.tzname(), "+100")
2564 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002565 self.assertTrue(base2.tzinfo is None)
2566 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002567
2568 # Ensure we can add one.
2569 base3 = base2.replace(tzinfo=z100)
2570 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002571 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002572
2573 # Out of bounds.
2574 base = cls(1)
2575 self.assertRaises(ValueError, base.replace, hour=24)
2576 self.assertRaises(ValueError, base.replace, minute=-1)
2577 self.assertRaises(ValueError, base.replace, second=100)
2578 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2579
Tim Peters60c76e42002-12-27 00:41:11 +00002580 def test_mixed_compare(self):
2581 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002582 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002583 self.assertEqual(t1, t2)
2584 t2 = t2.replace(tzinfo=None)
2585 self.assertEqual(t1, t2)
2586 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2587 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002588 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2589 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002590
Tim Peters0bf60bd2003-01-08 20:40:01 +00002591 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002592 class Varies(tzinfo):
2593 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002594 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002595 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002596 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002597 return self.offset
2598
2599 v = Varies()
2600 t1 = t2.replace(tzinfo=v)
2601 t2 = t2.replace(tzinfo=v)
2602 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2603 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2604 self.assertEqual(t1, t2)
2605
2606 # But if they're not identical, it isn't ignored.
2607 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002608 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002609
Tim Petersa98924a2003-05-17 05:55:19 +00002610 def test_subclass_timetz(self):
2611
2612 class C(self.theclass):
2613 theAnswer = 42
2614
2615 def __new__(cls, *args, **kws):
2616 temp = kws.copy()
2617 extra = temp.pop('extra')
2618 result = self.theclass.__new__(cls, *args, **temp)
2619 result.extra = extra
2620 return result
2621
2622 def newmeth(self, start):
2623 return start + self.hour + self.second
2624
2625 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2626
2627 dt1 = self.theclass(*args)
2628 dt2 = C(*args, **{'extra': 7})
2629
2630 self.assertEqual(dt2.__class__, C)
2631 self.assertEqual(dt2.theAnswer, 42)
2632 self.assertEqual(dt2.extra, 7)
2633 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2634 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2635
Tim Peters4c0db782002-12-26 05:01:19 +00002636
Tim Peters0bf60bd2003-01-08 20:40:01 +00002637# Testing datetime objects with a non-None tzinfo.
2638
Guido van Rossumd8faa362007-04-27 19:54:29 +00002639class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002640 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002641
2642 def test_trivial(self):
2643 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2644 self.assertEqual(dt.year, 1)
2645 self.assertEqual(dt.month, 2)
2646 self.assertEqual(dt.day, 3)
2647 self.assertEqual(dt.hour, 4)
2648 self.assertEqual(dt.minute, 5)
2649 self.assertEqual(dt.second, 6)
2650 self.assertEqual(dt.microsecond, 7)
2651 self.assertEqual(dt.tzinfo, None)
2652
2653 def test_even_more_compare(self):
2654 # The test_compare() and test_more_compare() inherited from TestDate
2655 # and TestDateTime covered non-tzinfo cases.
2656
2657 # Smallest possible after UTC adjustment.
2658 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2659 # Largest possible after UTC adjustment.
2660 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2661 tzinfo=FixedOffset(-1439, ""))
2662
2663 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002664 self.assertTrue(t1 < t2)
2665 self.assertTrue(t1 != t2)
2666 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002667
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002668 self.assertEqual(t1, t1)
2669 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002670
2671 # Equal afer adjustment.
2672 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2673 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2674 self.assertEqual(t1, t2)
2675
2676 # Change t1 not to subtract a minute, and t1 should be larger.
2677 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002678 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002679
2680 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2681 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002682 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002683
2684 # Back to the original t1, but make seconds resolve it.
2685 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2686 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002687 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002688
2689 # Likewise, but make microseconds resolve it.
2690 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2691 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002692 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002693
2694 # Make t2 naive and it should fail.
2695 t2 = self.theclass.min
2696 self.assertRaises(TypeError, lambda: t1 == t2)
2697 self.assertEqual(t2, t2)
2698
2699 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2700 class Naive(tzinfo):
2701 def utcoffset(self, dt): return None
2702 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2703 self.assertRaises(TypeError, lambda: t1 == t2)
2704 self.assertEqual(t2, t2)
2705
2706 # OTOH, it's OK to compare two of these mixing the two ways of being
2707 # naive.
2708 t1 = self.theclass(5, 6, 7)
2709 self.assertEqual(t1, t2)
2710
2711 # Try a bogus uctoffset.
2712 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002713 def utcoffset(self, dt):
2714 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002715 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2716 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002717 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002718
Tim Peters2a799bf2002-12-16 20:18:38 +00002719 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002720 # Try one without a tzinfo.
2721 args = 6, 7, 23, 20, 59, 1, 64**2
2722 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002723 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002724 green = pickler.dumps(orig, proto)
2725 derived = unpickler.loads(green)
2726 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002727
2728 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002729 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002730 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002731 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002732 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002733 green = pickler.dumps(orig, proto)
2734 derived = unpickler.loads(green)
2735 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002736 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002737 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2738 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002739
2740 def test_extreme_hashes(self):
2741 # If an attempt is made to hash these via subtracting the offset
2742 # then hashing a datetime object, OverflowError results. The
2743 # Python implementation used to blow up here.
2744 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2745 hash(t)
2746 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2747 tzinfo=FixedOffset(-1439, ""))
2748 hash(t)
2749
2750 # OTOH, an OOB offset should blow up.
2751 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2752 self.assertRaises(ValueError, hash, t)
2753
2754 def test_zones(self):
2755 est = FixedOffset(-300, "EST")
2756 utc = FixedOffset(0, "UTC")
2757 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002758 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2759 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2760 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002761 self.assertEqual(t1.tzinfo, est)
2762 self.assertEqual(t2.tzinfo, utc)
2763 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002764 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2765 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2766 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002767 self.assertEqual(t1.tzname(), "EST")
2768 self.assertEqual(t2.tzname(), "UTC")
2769 self.assertEqual(t3.tzname(), "MET")
2770 self.assertEqual(hash(t1), hash(t2))
2771 self.assertEqual(hash(t1), hash(t3))
2772 self.assertEqual(hash(t2), hash(t3))
2773 self.assertEqual(t1, t2)
2774 self.assertEqual(t1, t3)
2775 self.assertEqual(t2, t3)
2776 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2777 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2778 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002779 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002780 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2781 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2782 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2783
2784 def test_combine(self):
2785 met = FixedOffset(60, "MET")
2786 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002787 tz = time(18, 45, 3, 1234, tzinfo=met)
2788 dt = datetime.combine(d, tz)
2789 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002790 tzinfo=met))
2791
2792 def test_extract(self):
2793 met = FixedOffset(60, "MET")
2794 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2795 self.assertEqual(dt.date(), date(2002, 3, 4))
2796 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002797 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002798
2799 def test_tz_aware_arithmetic(self):
2800 import random
2801
2802 now = self.theclass.now()
2803 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002804 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002805 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002806 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002807 self.assertEqual(nowaware.timetz(), timeaware)
2808
2809 # Can't mix aware and non-aware.
2810 self.assertRaises(TypeError, lambda: now - nowaware)
2811 self.assertRaises(TypeError, lambda: nowaware - now)
2812
Tim Peters0bf60bd2003-01-08 20:40:01 +00002813 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002814 self.assertRaises(TypeError, lambda: now + nowaware)
2815 self.assertRaises(TypeError, lambda: nowaware + now)
2816 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2817
2818 # Subtracting should yield 0.
2819 self.assertEqual(now - now, timedelta(0))
2820 self.assertEqual(nowaware - nowaware, timedelta(0))
2821
2822 # Adding a delta should preserve tzinfo.
2823 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2824 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002825 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002826 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002827 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002828 self.assertEqual(nowawareplus, nowawareplus2)
2829
2830 # that - delta should be what we started with, and that - what we
2831 # started with should be delta.
2832 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002833 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002834 self.assertEqual(nowaware, diff)
2835 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2836 self.assertEqual(nowawareplus - nowaware, delta)
2837
2838 # Make up a random timezone.
2839 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002840 # Attach it to nowawareplus.
2841 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002842 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002843 # Make sure the difference takes the timezone adjustments into account.
2844 got = nowaware - nowawareplus
2845 # Expected: (nowaware base - nowaware offset) -
2846 # (nowawareplus base - nowawareplus offset) =
2847 # (nowaware base - nowawareplus base) +
2848 # (nowawareplus offset - nowaware offset) =
2849 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002850 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002851 self.assertEqual(got, expected)
2852
2853 # Try max possible difference.
2854 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2855 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2856 tzinfo=FixedOffset(-1439, "max"))
2857 maxdiff = max - min
2858 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2859 timedelta(minutes=2*1439))
2860
2861 def test_tzinfo_now(self):
2862 meth = self.theclass.now
2863 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2864 base = meth()
2865 # Try with and without naming the keyword.
2866 off42 = FixedOffset(42, "42")
2867 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002868 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002869 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002870 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002871 # Bad argument with and w/o naming the keyword.
2872 self.assertRaises(TypeError, meth, 16)
2873 self.assertRaises(TypeError, meth, tzinfo=16)
2874 # Bad keyword name.
2875 self.assertRaises(TypeError, meth, tinfo=off42)
2876 # Too many args.
2877 self.assertRaises(TypeError, meth, off42, off42)
2878
Tim Peters10cadce2003-01-23 19:58:02 +00002879 # We don't know which time zone we're in, and don't have a tzinfo
2880 # class to represent it, so seeing whether a tz argument actually
2881 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002882 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002883 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2884 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2885 for dummy in range(3):
2886 now = datetime.now(weirdtz)
2887 self.assertTrue(now.tzinfo is weirdtz)
2888 utcnow = datetime.utcnow().replace(tzinfo=utc)
2889 now2 = utcnow.astimezone(weirdtz)
2890 if abs(now - now2) < timedelta(seconds=30):
2891 break
2892 # Else the code is broken, or more than 30 seconds passed between
2893 # calls; assuming the latter, just try again.
2894 else:
2895 # Three strikes and we're out.
2896 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002897
Tim Peters2a799bf2002-12-16 20:18:38 +00002898 def test_tzinfo_fromtimestamp(self):
2899 import time
2900 meth = self.theclass.fromtimestamp
2901 ts = time.time()
2902 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2903 base = meth(ts)
2904 # Try with and without naming the keyword.
2905 off42 = FixedOffset(42, "42")
2906 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002907 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002908 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002909 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002910 # Bad argument with and w/o naming the keyword.
2911 self.assertRaises(TypeError, meth, ts, 16)
2912 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2913 # Bad keyword name.
2914 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2915 # Too many args.
2916 self.assertRaises(TypeError, meth, ts, off42, off42)
2917 # Too few args.
2918 self.assertRaises(TypeError, meth)
2919
Tim Peters2a44a8d2003-01-23 20:53:10 +00002920 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002921 timestamp = 1000000000
2922 utcdatetime = datetime.utcfromtimestamp(timestamp)
2923 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2924 # But on some flavor of Mac, it's nowhere near that. So we can't have
2925 # any idea here what time that actually is, we can only test that
2926 # relative changes match.
2927 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2928 tz = FixedOffset(utcoffset, "tz", 0)
2929 expected = utcdatetime + utcoffset
2930 got = datetime.fromtimestamp(timestamp, tz)
2931 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002932
Tim Peters2a799bf2002-12-16 20:18:38 +00002933 def test_tzinfo_utcnow(self):
2934 meth = self.theclass.utcnow
2935 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2936 base = meth()
2937 # Try with and without naming the keyword; for whatever reason,
2938 # utcnow() doesn't accept a tzinfo argument.
2939 off42 = FixedOffset(42, "42")
2940 self.assertRaises(TypeError, meth, off42)
2941 self.assertRaises(TypeError, meth, tzinfo=off42)
2942
2943 def test_tzinfo_utcfromtimestamp(self):
2944 import time
2945 meth = self.theclass.utcfromtimestamp
2946 ts = time.time()
2947 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2948 base = meth(ts)
2949 # Try with and without naming the keyword; for whatever reason,
2950 # utcfromtimestamp() doesn't accept a tzinfo argument.
2951 off42 = FixedOffset(42, "42")
2952 self.assertRaises(TypeError, meth, ts, off42)
2953 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2954
2955 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002956 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002957 # DST flag.
2958 class DST(tzinfo):
2959 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002960 if isinstance(dstvalue, int):
2961 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002962 self.dstvalue = dstvalue
2963 def dst(self, dt):
2964 return self.dstvalue
2965
2966 cls = self.theclass
2967 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2968 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2969 t = d.timetuple()
2970 self.assertEqual(1, t.tm_year)
2971 self.assertEqual(1, t.tm_mon)
2972 self.assertEqual(1, t.tm_mday)
2973 self.assertEqual(10, t.tm_hour)
2974 self.assertEqual(20, t.tm_min)
2975 self.assertEqual(30, t.tm_sec)
2976 self.assertEqual(0, t.tm_wday)
2977 self.assertEqual(1, t.tm_yday)
2978 self.assertEqual(flag, t.tm_isdst)
2979
2980 # dst() returns wrong type.
2981 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2982
2983 # dst() at the edge.
2984 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2985 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2986
2987 # dst() out of range.
2988 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2989 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2990
2991 def test_utctimetuple(self):
2992 class DST(tzinfo):
2993 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002994 if isinstance(dstvalue, int):
2995 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002996 self.dstvalue = dstvalue
2997 def dst(self, dt):
2998 return self.dstvalue
2999
3000 cls = self.theclass
3001 # This can't work: DST didn't implement utcoffset.
3002 self.assertRaises(NotImplementedError,
3003 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3004
3005 class UOFS(DST):
3006 def __init__(self, uofs, dofs=None):
3007 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00003008 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00003009 def utcoffset(self, dt):
3010 return self.uofs
3011
Tim Peters2a799bf2002-12-16 20:18:38 +00003012 for dstvalue in -33, 33, 0, None:
3013 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3014 t = d.utctimetuple()
3015 self.assertEqual(d.year, t.tm_year)
3016 self.assertEqual(d.month, t.tm_mon)
3017 self.assertEqual(d.day, t.tm_mday)
3018 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3019 self.assertEqual(13, t.tm_min)
3020 self.assertEqual(d.second, t.tm_sec)
3021 self.assertEqual(d.weekday(), t.tm_wday)
3022 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3023 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003024 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3025 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003026 self.assertEqual(0, t.tm_isdst)
3027
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003028 # Check that utctimetuple() is the same as
3029 # astimezone(utc).timetuple()
3030 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3031 for tz in [timezone.min, timezone.utc, timezone.max]:
3032 dtz = d.replace(tzinfo=tz)
3033 self.assertEqual(dtz.utctimetuple()[:-1],
3034 dtz.astimezone(timezone.utc).timetuple()[:-1])
3035 # At the edges, UTC adjustment can produce years out-of-range
3036 # for a datetime object. Ensure that an OverflowError is
3037 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003038 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3039 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003040 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003041
3042 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3043 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003044 self.assertRaises(OverflowError, huge.utctimetuple)
3045 # More overflow cases
3046 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3047 self.assertRaises(OverflowError, tiny.utctimetuple)
3048 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3049 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003050
3051 def test_tzinfo_isoformat(self):
3052 zero = FixedOffset(0, "+00:00")
3053 plus = FixedOffset(220, "+03:40")
3054 minus = FixedOffset(-231, "-03:51")
3055 unknown = FixedOffset(None, "")
3056
3057 cls = self.theclass
3058 datestr = '0001-02-03'
3059 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003060 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003061 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3062 timestr = '04:05:59' + (us and '.987001' or '')
3063 ofsstr = ofs is not None and d.tzname() or ''
3064 tailstr = timestr + ofsstr
3065 iso = d.isoformat()
3066 self.assertEqual(iso, datestr + 'T' + tailstr)
3067 self.assertEqual(iso, d.isoformat('T'))
3068 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003069 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003070 self.assertEqual(str(d), datestr + ' ' + tailstr)
3071
Tim Peters12bf3392002-12-24 05:41:27 +00003072 def test_replace(self):
3073 cls = self.theclass
3074 z100 = FixedOffset(100, "+100")
3075 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3076 args = [1, 2, 3, 4, 5, 6, 7, z100]
3077 base = cls(*args)
3078 self.assertEqual(base, base.replace())
3079
3080 i = 0
3081 for name, newval in (("year", 2),
3082 ("month", 3),
3083 ("day", 4),
3084 ("hour", 5),
3085 ("minute", 6),
3086 ("second", 7),
3087 ("microsecond", 8),
3088 ("tzinfo", zm200)):
3089 newargs = args[:]
3090 newargs[i] = newval
3091 expected = cls(*newargs)
3092 got = base.replace(**{name: newval})
3093 self.assertEqual(expected, got)
3094 i += 1
3095
3096 # Ensure we can get rid of a tzinfo.
3097 self.assertEqual(base.tzname(), "+100")
3098 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003099 self.assertTrue(base2.tzinfo is None)
3100 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003101
3102 # Ensure we can add one.
3103 base3 = base2.replace(tzinfo=z100)
3104 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003105 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003106
3107 # Out of bounds.
3108 base = cls(2000, 2, 29)
3109 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003110
Tim Peters80475bb2002-12-25 07:40:55 +00003111 def test_more_astimezone(self):
3112 # The inherited test_astimezone covered some trivial and error cases.
3113 fnone = FixedOffset(None, "None")
3114 f44m = FixedOffset(44, "44")
3115 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3116
Tim Peters10cadce2003-01-23 19:58:02 +00003117 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003118 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003119 # Replacing with degenerate tzinfo raises an exception.
3120 self.assertRaises(ValueError, dt.astimezone, fnone)
3121 # Ditto with None tz.
3122 self.assertRaises(TypeError, dt.astimezone, None)
3123 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003124 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003125 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003126 self.assertEqual(x.date(), dt.date())
3127 self.assertEqual(x.time(), dt.time())
3128
3129 # Replacing with different tzinfo does adjust.
3130 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003131 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003132 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3133 expected = dt - dt.utcoffset() # in effect, convert to UTC
3134 expected += fm5h.utcoffset(dt) # and from there to local time
3135 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3136 self.assertEqual(got.date(), expected.date())
3137 self.assertEqual(got.time(), expected.time())
3138 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003139 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003140 self.assertEqual(got, expected)
3141
Tim Peters4c0db782002-12-26 05:01:19 +00003142 def test_aware_subtract(self):
3143 cls = self.theclass
3144
Tim Peters60c76e42002-12-27 00:41:11 +00003145 # Ensure that utcoffset() is ignored when the operands have the
3146 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003147 class OperandDependentOffset(tzinfo):
3148 def utcoffset(self, t):
3149 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003150 # d0 and d1 equal after adjustment
3151 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003152 else:
Tim Peters397301e2003-01-02 21:28:08 +00003153 # d2 off in the weeds
3154 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003155
3156 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3157 d0 = base.replace(minute=3)
3158 d1 = base.replace(minute=9)
3159 d2 = base.replace(minute=11)
3160 for x in d0, d1, d2:
3161 for y in d0, d1, d2:
3162 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003163 expected = timedelta(minutes=x.minute - y.minute)
3164 self.assertEqual(got, expected)
3165
3166 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3167 # ignored.
3168 base = cls(8, 9, 10, 11, 12, 13, 14)
3169 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3170 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3171 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3172 for x in d0, d1, d2:
3173 for y in d0, d1, d2:
3174 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003175 if (x is d0 or x is d1) and (y is d0 or y is d1):
3176 expected = timedelta(0)
3177 elif x is y is d2:
3178 expected = timedelta(0)
3179 elif x is d2:
3180 expected = timedelta(minutes=(11-59)-0)
3181 else:
3182 assert y is d2
3183 expected = timedelta(minutes=0-(11-59))
3184 self.assertEqual(got, expected)
3185
Tim Peters60c76e42002-12-27 00:41:11 +00003186 def test_mixed_compare(self):
3187 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003188 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003189 self.assertEqual(t1, t2)
3190 t2 = t2.replace(tzinfo=None)
3191 self.assertEqual(t1, t2)
3192 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3193 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003194 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3195 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003196
Tim Peters0bf60bd2003-01-08 20:40:01 +00003197 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003198 class Varies(tzinfo):
3199 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003200 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003201 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003202 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003203 return self.offset
3204
3205 v = Varies()
3206 t1 = t2.replace(tzinfo=v)
3207 t2 = t2.replace(tzinfo=v)
3208 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3209 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3210 self.assertEqual(t1, t2)
3211
3212 # But if they're not identical, it isn't ignored.
3213 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003214 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003215
Tim Petersa98924a2003-05-17 05:55:19 +00003216 def test_subclass_datetimetz(self):
3217
3218 class C(self.theclass):
3219 theAnswer = 42
3220
3221 def __new__(cls, *args, **kws):
3222 temp = kws.copy()
3223 extra = temp.pop('extra')
3224 result = self.theclass.__new__(cls, *args, **temp)
3225 result.extra = extra
3226 return result
3227
3228 def newmeth(self, start):
3229 return start + self.hour + self.year
3230
3231 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3232
3233 dt1 = self.theclass(*args)
3234 dt2 = C(*args, **{'extra': 7})
3235
3236 self.assertEqual(dt2.__class__, C)
3237 self.assertEqual(dt2.theAnswer, 42)
3238 self.assertEqual(dt2.extra, 7)
3239 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3240 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3241
Tim Peters621818b2002-12-29 23:44:49 +00003242# Pain to set up DST-aware tzinfo classes.
3243
3244def first_sunday_on_or_after(dt):
3245 days_to_go = 6 - dt.weekday()
3246 if days_to_go:
3247 dt += timedelta(days_to_go)
3248 return dt
3249
3250ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003251MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003252HOUR = timedelta(hours=1)
3253DAY = timedelta(days=1)
3254# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3255DSTSTART = datetime(1, 4, 1, 2)
3256# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003257# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3258# being standard time on that day, there is no spelling in local time of
3259# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3260DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003261
3262class USTimeZone(tzinfo):
3263
3264 def __init__(self, hours, reprname, stdname, dstname):
3265 self.stdoffset = timedelta(hours=hours)
3266 self.reprname = reprname
3267 self.stdname = stdname
3268 self.dstname = dstname
3269
3270 def __repr__(self):
3271 return self.reprname
3272
3273 def tzname(self, dt):
3274 if self.dst(dt):
3275 return self.dstname
3276 else:
3277 return self.stdname
3278
3279 def utcoffset(self, dt):
3280 return self.stdoffset + self.dst(dt)
3281
3282 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003283 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003284 # An exception instead may be sensible here, in one or more of
3285 # the cases.
3286 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003287 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003288
3289 # Find first Sunday in April.
3290 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3291 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3292
3293 # Find last Sunday in October.
3294 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3295 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3296
Tim Peters621818b2002-12-29 23:44:49 +00003297 # Can't compare naive to aware objects, so strip the timezone from
3298 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003299 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003300 return HOUR
3301 else:
3302 return ZERO
3303
Tim Peters521fc152002-12-31 17:36:56 +00003304Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3305Central = USTimeZone(-6, "Central", "CST", "CDT")
3306Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3307Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003308utc_real = FixedOffset(0, "UTC", 0)
3309# For better test coverage, we want another flavor of UTC that's west of
3310# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003311utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003312
3313class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003314 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003315 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003316 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003317
Tim Peters0bf60bd2003-01-08 20:40:01 +00003318 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003319
Tim Peters521fc152002-12-31 17:36:56 +00003320 # Check a time that's inside DST.
3321 def checkinside(self, dt, tz, utc, dston, dstoff):
3322 self.assertEqual(dt.dst(), HOUR)
3323
3324 # Conversion to our own timezone is always an identity.
3325 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003326
3327 asutc = dt.astimezone(utc)
3328 there_and_back = asutc.astimezone(tz)
3329
3330 # Conversion to UTC and back isn't always an identity here,
3331 # because there are redundant spellings (in local time) of
3332 # UTC time when DST begins: the clock jumps from 1:59:59
3333 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3334 # make sense then. The classes above treat 2:MM:SS as
3335 # daylight time then (it's "after 2am"), really an alias
3336 # for 1:MM:SS standard time. The latter form is what
3337 # conversion back from UTC produces.
3338 if dt.date() == dston.date() and dt.hour == 2:
3339 # We're in the redundant hour, and coming back from
3340 # UTC gives the 1:MM:SS standard-time spelling.
3341 self.assertEqual(there_and_back + HOUR, dt)
3342 # Although during was considered to be in daylight
3343 # time, there_and_back is not.
3344 self.assertEqual(there_and_back.dst(), ZERO)
3345 # They're the same times in UTC.
3346 self.assertEqual(there_and_back.astimezone(utc),
3347 dt.astimezone(utc))
3348 else:
3349 # We're not in the redundant hour.
3350 self.assertEqual(dt, there_and_back)
3351
Tim Peters327098a2003-01-20 22:54:38 +00003352 # Because we have a redundant spelling when DST begins, there is
3353 # (unforunately) an hour when DST ends that can't be spelled at all in
3354 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3355 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3356 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3357 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3358 # expressed in local time. Nevertheless, we want conversion back
3359 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003360 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003361 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003362 if dt.date() == dstoff.date() and dt.hour == 0:
3363 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003364 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003365 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3366 nexthour_utc += HOUR
3367 nexthour_tz = nexthour_utc.astimezone(tz)
3368 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003369 else:
Tim Peters327098a2003-01-20 22:54:38 +00003370 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003371
3372 # Check a time that's outside DST.
3373 def checkoutside(self, dt, tz, utc):
3374 self.assertEqual(dt.dst(), ZERO)
3375
3376 # Conversion to our own timezone is always an identity.
3377 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003378
3379 # Converting to UTC and back is an identity too.
3380 asutc = dt.astimezone(utc)
3381 there_and_back = asutc.astimezone(tz)
3382 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003383
Tim Peters1024bf82002-12-30 17:09:40 +00003384 def convert_between_tz_and_utc(self, tz, utc):
3385 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003386 # Because 1:MM on the day DST ends is taken as being standard time,
3387 # there is no spelling in tz for the last hour of daylight time.
3388 # For purposes of the test, the last hour of DST is 0:MM, which is
3389 # taken as being daylight time (and 1:MM is taken as being standard
3390 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003391 dstoff = self.dstoff.replace(tzinfo=tz)
3392 for delta in (timedelta(weeks=13),
3393 DAY,
3394 HOUR,
3395 timedelta(minutes=1),
3396 timedelta(microseconds=1)):
3397
Tim Peters521fc152002-12-31 17:36:56 +00003398 self.checkinside(dston, tz, utc, dston, dstoff)
3399 for during in dston + delta, dstoff - delta:
3400 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003401
Tim Peters521fc152002-12-31 17:36:56 +00003402 self.checkoutside(dstoff, tz, utc)
3403 for outside in dston - delta, dstoff + delta:
3404 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003405
Tim Peters621818b2002-12-29 23:44:49 +00003406 def test_easy(self):
3407 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003408 self.convert_between_tz_and_utc(Eastern, utc_real)
3409 self.convert_between_tz_and_utc(Pacific, utc_real)
3410 self.convert_between_tz_and_utc(Eastern, utc_fake)
3411 self.convert_between_tz_and_utc(Pacific, utc_fake)
3412 # The next is really dancing near the edge. It works because
3413 # Pacific and Eastern are far enough apart that their "problem
3414 # hours" don't overlap.
3415 self.convert_between_tz_and_utc(Eastern, Pacific)
3416 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003417 # OTOH, these fail! Don't enable them. The difficulty is that
3418 # the edge case tests assume that every hour is representable in
3419 # the "utc" class. This is always true for a fixed-offset tzinfo
3420 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3421 # For these adjacent DST-aware time zones, the range of time offsets
3422 # tested ends up creating hours in the one that aren't representable
3423 # in the other. For the same reason, we would see failures in the
3424 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3425 # offset deltas in convert_between_tz_and_utc().
3426 #
3427 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3428 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003429
Tim Petersf3615152003-01-01 21:51:37 +00003430 def test_tricky(self):
3431 # 22:00 on day before daylight starts.
3432 fourback = self.dston - timedelta(hours=4)
3433 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003434 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003435 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3436 # 2", we should get the 3 spelling.
3437 # If we plug 22:00 the day before into Eastern, it "looks like std
3438 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3439 # to 22:00 lands on 2:00, which makes no sense in local time (the
3440 # local clock jumps from 1 to 3). The point here is to make sure we
3441 # get the 3 spelling.
3442 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003443 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003444 self.assertEqual(expected, got)
3445
3446 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3447 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003448 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003449 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3450 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3451 # spelling.
3452 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003453 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003454 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003455
Tim Petersadf64202003-01-04 06:03:15 +00003456 # Now on the day DST ends, we want "repeat an hour" behavior.
3457 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3458 # EST 23:MM 0:MM 1:MM 2:MM
3459 # EDT 0:MM 1:MM 2:MM 3:MM
3460 # wall 0:MM 1:MM 1:MM 2:MM against these
3461 for utc in utc_real, utc_fake:
3462 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003463 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003464 # Convert that to UTC.
3465 first_std_hour -= tz.utcoffset(None)
3466 # Adjust for possibly fake UTC.
3467 asutc = first_std_hour + utc.utcoffset(None)
3468 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3469 # tz=Eastern.
3470 asutcbase = asutc.replace(tzinfo=utc)
3471 for tzhour in (0, 1, 1, 2):
3472 expectedbase = self.dstoff.replace(hour=tzhour)
3473 for minute in 0, 30, 59:
3474 expected = expectedbase.replace(minute=minute)
3475 asutc = asutcbase.replace(minute=minute)
3476 astz = asutc.astimezone(tz)
3477 self.assertEqual(astz.replace(tzinfo=None), expected)
3478 asutcbase += HOUR
3479
3480
Tim Peters710fb152003-01-02 19:35:54 +00003481 def test_bogus_dst(self):
3482 class ok(tzinfo):
3483 def utcoffset(self, dt): return HOUR
3484 def dst(self, dt): return HOUR
3485
3486 now = self.theclass.now().replace(tzinfo=utc_real)
3487 # Doesn't blow up.
3488 now.astimezone(ok())
3489
3490 # Does blow up.
3491 class notok(ok):
3492 def dst(self, dt): return None
3493 self.assertRaises(ValueError, now.astimezone, notok())
3494
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003495 # Sometimes blow up. In the following, tzinfo.dst()
3496 # implementation may return None or not Nonedepending on
3497 # whether DST is assumed to be in effect. In this situation,
3498 # a ValueError should be raised by astimezone().
3499 class tricky_notok(ok):
3500 def dst(self, dt):
3501 if dt.year == 2000:
3502 return None
3503 else:
3504 return 10*HOUR
3505 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3506 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3507
Tim Peters52dcce22003-01-23 16:36:11 +00003508 def test_fromutc(self):
3509 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3510 now = datetime.utcnow().replace(tzinfo=utc_real)
3511 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3512 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3513 enow = Eastern.fromutc(now) # doesn't blow up
3514 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3515 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3516 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3517
3518 # Always converts UTC to standard time.
3519 class FauxUSTimeZone(USTimeZone):
3520 def fromutc(self, dt):
3521 return dt + self.stdoffset
3522 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3523
3524 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3525 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3526 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3527
3528 # Check around DST start.
3529 start = self.dston.replace(hour=4, tzinfo=Eastern)
3530 fstart = start.replace(tzinfo=FEastern)
3531 for wall in 23, 0, 1, 3, 4, 5:
3532 expected = start.replace(hour=wall)
3533 if wall == 23:
3534 expected -= timedelta(days=1)
3535 got = Eastern.fromutc(start)
3536 self.assertEqual(expected, got)
3537
3538 expected = fstart + FEastern.stdoffset
3539 got = FEastern.fromutc(fstart)
3540 self.assertEqual(expected, got)
3541
3542 # Ensure astimezone() calls fromutc() too.
3543 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3544 self.assertEqual(expected, got)
3545
3546 start += HOUR
3547 fstart += HOUR
3548
3549 # Check around DST end.
3550 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3551 fstart = start.replace(tzinfo=FEastern)
3552 for wall in 0, 1, 1, 2, 3, 4:
3553 expected = start.replace(hour=wall)
3554 got = Eastern.fromutc(start)
3555 self.assertEqual(expected, got)
3556
3557 expected = fstart + FEastern.stdoffset
3558 got = FEastern.fromutc(fstart)
3559 self.assertEqual(expected, got)
3560
3561 # Ensure astimezone() calls fromutc() too.
3562 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3563 self.assertEqual(expected, got)
3564
3565 start += HOUR
3566 fstart += HOUR
3567
Tim Peters710fb152003-01-02 19:35:54 +00003568
Tim Peters528ca532004-09-16 01:30:50 +00003569#############################################################################
3570# oddballs
3571
3572class Oddballs(unittest.TestCase):
3573
3574 def test_bug_1028306(self):
3575 # Trying to compare a date to a datetime should act like a mixed-
3576 # type comparison, despite that datetime is a subclass of date.
3577 as_date = date.today()
3578 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003579 self.assertTrue(as_date != as_datetime)
3580 self.assertTrue(as_datetime != as_date)
3581 self.assertTrue(not as_date == as_datetime)
3582 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003583 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3584 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3585 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3586 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3587 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3588 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3589 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3590 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3591
3592 # Neverthelss, comparison should work with the base-class (date)
3593 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003594 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003595 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003596 as_different = as_datetime.replace(day= different_day)
3597 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003598
3599 # And date should compare with other subclasses of date. If a
3600 # subclass wants to stop this, it's up to the subclass to do so.
3601 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3602 self.assertEqual(as_date, date_sc)
3603 self.assertEqual(date_sc, as_date)
3604
3605 # Ditto for datetimes.
3606 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3607 as_date.day, 0, 0, 0)
3608 self.assertEqual(as_datetime, datetime_sc)
3609 self.assertEqual(datetime_sc, as_datetime)
3610
Tim Peters2a799bf2002-12-16 20:18:38 +00003611def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003612 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003613
3614if __name__ == "__main__":
3615 test_main()