blob: 4423d9b26b1130846a3444f3d54f691c59e85153 [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
Alexander Belopolsky33b94c92010-06-23 22:29:48 +000022pickle_choices = [(pickle, pickle, proto)
23 for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
24assert len(pickle_choices) == pickle.HIGHEST_PROTOCOL + 1
Guido van Rossum177e41a2003-01-30 22:06:23 +000025
Tim Peters68124bb2003-02-08 03:46:31 +000026# An arbitrary collection of objects of non-datetime types, for testing
27# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000028OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000029
Tim Peters2a799bf2002-12-16 20:18:38 +000030
Alexander Belopolsky1790bc42010-05-31 17:33:47 +000031# XXX Copied from test_float.
32INF = float("inf")
33NAN = float("nan")
34
35# decorator for skipping tests on non-IEEE 754 platforms
36requires_IEEE_754 = unittest.skipUnless(
37 float.__getformat__("double").startswith("IEEE"),
38 "test requires IEEE 754 doubles")
39
40
Tim Peters2a799bf2002-12-16 20:18:38 +000041#############################################################################
42# module tests
43
44class TestModule(unittest.TestCase):
45
46 def test_constants(self):
47 import datetime
48 self.assertEqual(datetime.MINYEAR, 1)
49 self.assertEqual(datetime.MAXYEAR, 9999)
50
51#############################################################################
52# tzinfo tests
53
54class FixedOffset(tzinfo):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000055
Tim Peters2a799bf2002-12-16 20:18:38 +000056 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000057 if isinstance(offset, int):
58 offset = timedelta(minutes=offset)
59 if isinstance(dstoffset, int):
60 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000061 self.__offset = offset
62 self.__name = name
63 self.__dstoffset = dstoffset
64 def __repr__(self):
65 return self.__name.lower()
66 def utcoffset(self, dt):
67 return self.__offset
68 def tzname(self, dt):
69 return self.__name
70 def dst(self, dt):
71 return self.__dstoffset
72
Tim Petersfb8472c2002-12-21 05:04:42 +000073class PicklableFixedOffset(FixedOffset):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000074
Tim Petersfb8472c2002-12-21 05:04:42 +000075 def __init__(self, offset=None, name=None, dstoffset=None):
76 FixedOffset.__init__(self, offset, name, dstoffset)
77
Tim Peters2a799bf2002-12-16 20:18:38 +000078class TestTZInfo(unittest.TestCase):
79
80 def test_non_abstractness(self):
81 # In order to allow subclasses to get pickled, the C implementation
82 # wasn't able to get away with having __init__ raise
83 # NotImplementedError.
84 useless = tzinfo()
85 dt = datetime.max
86 self.assertRaises(NotImplementedError, useless.tzname, dt)
87 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
88 self.assertRaises(NotImplementedError, useless.dst, dt)
89
90 def test_subclass_must_override(self):
91 class NotEnough(tzinfo):
92 def __init__(self, offset, name):
93 self.__offset = offset
94 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000095 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000096 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000097 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000098
99 dt = datetime.now()
100 self.assertRaises(NotImplementedError, ne.tzname, dt)
101 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
102 self.assertRaises(NotImplementedError, ne.dst, dt)
103
104 def test_normal(self):
105 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +0000106 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000107 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000108 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000110 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000111
112 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000113 # There's no point to pickling tzinfo objects on their own (they
114 # carry no data), but they need to be picklable anyway else
115 # concrete subclasses can't be pickled.
116 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000117 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000118 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000119 green = pickler.dumps(orig, proto)
120 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000121 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000122
123 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000124 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000125 offset = timedelta(minutes=-300)
Alexander Belopolsky1b7046b2010-06-23 21:40:15 +0000126 for otype, args in [
127 (PicklableFixedOffset, (offset, 'cookie')),
128 (timezone, (offset,)),
129 (timezone, (offset, "EST"))]:
130 orig = otype(*args)
131 oname = orig.tzname(None)
132 self.assertIsInstance(orig, tzinfo)
133 self.assertIs(type(orig), otype)
134 self.assertEqual(orig.utcoffset(None), offset)
135 self.assertEqual(orig.tzname(None), oname)
136 for pickler, unpickler, proto in pickle_choices:
137 green = pickler.dumps(orig, proto)
138 derived = unpickler.loads(green)
139 self.assertIsInstance(derived, tzinfo)
140 self.assertIs(type(derived), otype)
141 self.assertEqual(derived.utcoffset(None), offset)
142 self.assertEqual(derived.tzname(None), oname)
Tim Peters2a799bf2002-12-16 20:18:38 +0000143
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000144class TestTimeZone(unittest.TestCase):
145
146 def setUp(self):
147 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
148 self.EST = timezone(-timedelta(hours=5), 'EST')
149 self.DT = datetime(2010, 1, 1)
150
151 def test_str(self):
152 for tz in [self.ACDT, self.EST, timezone.utc,
153 timezone.min, timezone.max]:
154 self.assertEqual(str(tz), tz.tzname(None))
155
156 def test_class_members(self):
157 limit = timedelta(hours=23, minutes=59)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000158 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
159 self.assertEqual(timezone.min.utcoffset(None), -limit)
160 self.assertEqual(timezone.max.utcoffset(None), limit)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000161
162
163 def test_constructor(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000164 self.assertEqual(timezone.utc, timezone(timedelta(0)))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000165 # invalid offsets
166 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
167 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
168 self.assertRaises(ValueError, timezone, invalid)
169 self.assertRaises(ValueError, timezone, -invalid)
170
171 with self.assertRaises(TypeError): timezone(None)
172 with self.assertRaises(TypeError): timezone(42)
173 with self.assertRaises(TypeError): timezone(ZERO, None)
174 with self.assertRaises(TypeError): timezone(ZERO, 42)
Alexander Belopolsky5e307de2010-06-23 22:58:49 +0000175 with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000176
177 def test_inheritance(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000178 self.assertIsInstance(timezone.utc, tzinfo)
179 self.assertIsInstance(self.EST, tzinfo)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000180
181 def test_utcoffset(self):
182 dummy = self.DT
183 for h in [0, 1.5, 12]:
184 offset = h * HOUR
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000185 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
186 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000187
188 with self.assertRaises(TypeError): self.EST.utcoffset('')
189 with self.assertRaises(TypeError): self.EST.utcoffset(5)
190
191
192 def test_dst(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000193 self.assertEqual(None, timezone.utc.dst(self.DT))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000194
195 with self.assertRaises(TypeError): self.EST.dst('')
196 with self.assertRaises(TypeError): self.EST.dst(5)
197
198 def test_tzname(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000199 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
200 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
201 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
202 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
203 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000204
205 with self.assertRaises(TypeError): self.EST.tzname('')
206 with self.assertRaises(TypeError): self.EST.tzname(5)
207
208 def test_fromutc(self):
209 with self.assertRaises(ValueError):
210 timezone.utc.fromutc(self.DT)
Alexander Belopolsky5e307de2010-06-23 22:58:49 +0000211 with self.assertRaises(TypeError):
212 timezone.utc.fromutc('not datetime')
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000213 for tz in [self.EST, self.ACDT, Eastern]:
214 utctime = self.DT.replace(tzinfo=tz)
215 local = tz.fromutc(utctime)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000216 self.assertEqual(local - utctime, tz.utcoffset(local))
217 self.assertEqual(local,
218 self.DT.replace(tzinfo=timezone.utc))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000219
220 def test_comparison(self):
221 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
222 self.assertEqual(timezone(HOUR), timezone(HOUR))
223 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
224 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
225 self.assertIn(timezone(ZERO), {timezone(ZERO)})
226
227 def test_aware_datetime(self):
228 # test that timezone instances can be used by datetime
229 t = datetime(1, 1, 1)
230 for tz in [timezone.min, timezone.max, timezone.utc]:
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000231 self.assertEqual(tz.tzname(t),
232 t.replace(tzinfo=tz).tzname())
233 self.assertEqual(tz.utcoffset(t),
234 t.replace(tzinfo=tz).utcoffset())
235 self.assertEqual(tz.dst(t),
236 t.replace(tzinfo=tz).dst())
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000237
Tim Peters2a799bf2002-12-16 20:18:38 +0000238#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000239# Base clase for testing a particular aspect of timedelta, time, date and
240# datetime comparisons.
241
Guido van Rossumd8faa362007-04-27 19:54:29 +0000242class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000243 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
244
245 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
246 # legit constructor.
247
248 def test_harmless_mixed_comparison(self):
249 me = self.theclass(1, 1, 1)
250
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000251 self.assertFalse(me == ())
252 self.assertTrue(me != ())
253 self.assertFalse(() == me)
254 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000255
Benjamin Peterson577473f2010-01-19 00:09:57 +0000256 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000257 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000258
259 def test_harmful_mixed_comparison(self):
260 me = self.theclass(1, 1, 1)
261
262 self.assertRaises(TypeError, lambda: me < ())
263 self.assertRaises(TypeError, lambda: me <= ())
264 self.assertRaises(TypeError, lambda: me > ())
265 self.assertRaises(TypeError, lambda: me >= ())
266
267 self.assertRaises(TypeError, lambda: () < me)
268 self.assertRaises(TypeError, lambda: () <= me)
269 self.assertRaises(TypeError, lambda: () > me)
270 self.assertRaises(TypeError, lambda: () >= me)
271
Tim Peters07534a62003-02-07 22:50:28 +0000272#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000273# timedelta tests
274
Guido van Rossumd8faa362007-04-27 19:54:29 +0000275class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000276
277 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000278
279 def test_constructor(self):
280 eq = self.assertEqual
281 td = timedelta
282
283 # Check keyword args to constructor
284 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
285 milliseconds=0, microseconds=0))
286 eq(td(1), td(days=1))
287 eq(td(0, 1), td(seconds=1))
288 eq(td(0, 0, 1), td(microseconds=1))
289 eq(td(weeks=1), td(days=7))
290 eq(td(days=1), td(hours=24))
291 eq(td(hours=1), td(minutes=60))
292 eq(td(minutes=1), td(seconds=60))
293 eq(td(seconds=1), td(milliseconds=1000))
294 eq(td(milliseconds=1), td(microseconds=1000))
295
296 # Check float args to constructor
297 eq(td(weeks=1.0/7), td(days=1))
298 eq(td(days=1.0/24), td(hours=1))
299 eq(td(hours=1.0/60), td(minutes=1))
300 eq(td(minutes=1.0/60), td(seconds=1))
301 eq(td(seconds=0.001), td(milliseconds=1))
302 eq(td(milliseconds=0.001), td(microseconds=1))
303
304 def test_computations(self):
305 eq = self.assertEqual
306 td = timedelta
307
308 a = td(7) # One week
309 b = td(0, 60) # One minute
310 c = td(0, 0, 1000) # One millisecond
311 eq(a+b+c, td(7, 60, 1000))
312 eq(a-b, td(6, 24*3600 - 60))
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000313 eq(b.__rsub__(a), td(6, 24*3600 - 60))
Tim Peters2a799bf2002-12-16 20:18:38 +0000314 eq(-a, td(-7))
315 eq(+a, td(7))
316 eq(-b, td(-1, 24*3600 - 60))
317 eq(-c, td(-1, 24*3600 - 1, 999000))
318 eq(abs(a), a)
319 eq(abs(-a), a)
320 eq(td(6, 24*3600), a)
321 eq(td(0, 0, 60*1000000), b)
322 eq(a*10, td(70))
323 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000324 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000325 eq(b*10, td(0, 600))
326 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000327 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000328 eq(c*10, td(0, 0, 10000))
329 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000330 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000331 eq(a*-1, -a)
332 eq(b*-2, -b-b)
333 eq(c*-2, -c+-c)
334 eq(b*(60*24), (b*60)*24)
335 eq(b*(60*24), (60*b)*24)
336 eq(c*1000, td(0, 1))
337 eq(1000*c, td(0, 1))
338 eq(a//7, td(1))
339 eq(b//10, td(0, 6))
340 eq(c//1000, td(0, 0, 1))
341 eq(a//10, td(0, 7*24*360))
342 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000343 eq(a/0.5, td(14))
344 eq(b/0.5, td(0, 120))
345 eq(a/7, td(1))
346 eq(b/10, td(0, 6))
347 eq(c/1000, td(0, 0, 1))
348 eq(a/10, td(0, 7*24*360))
349 eq(a/3600000, td(0, 0, 7*24*1000))
350
351 # Multiplication by float
352 us = td(microseconds=1)
353 eq((3*us) * 0.5, 2*us)
354 eq((5*us) * 0.5, 2*us)
355 eq(0.5 * (3*us), 2*us)
356 eq(0.5 * (5*us), 2*us)
357 eq((-3*us) * 0.5, -2*us)
358 eq((-5*us) * 0.5, -2*us)
359
360 # Division by int and float
361 eq((3*us) / 2, 2*us)
362 eq((5*us) / 2, 2*us)
363 eq((-3*us) / 2.0, -2*us)
364 eq((-5*us) / 2.0, -2*us)
365 eq((3*us) / -2, -2*us)
366 eq((5*us) / -2, -2*us)
367 eq((3*us) / -2.0, -2*us)
368 eq((5*us) / -2.0, -2*us)
369 for i in range(-10, 10):
370 eq((i*us/3)//us, round(i/3))
371 for i in range(-10, 10):
372 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000373
374 def test_disallowed_computations(self):
375 a = timedelta(42)
376
Mark Dickinson5c2db372009-12-05 20:28:34 +0000377 # Add/sub ints or floats should be illegal
378 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000379 self.assertRaises(TypeError, lambda: a+i)
380 self.assertRaises(TypeError, lambda: a-i)
381 self.assertRaises(TypeError, lambda: i+a)
382 self.assertRaises(TypeError, lambda: i-a)
383
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000384 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000385 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000386 zero = 0
387 self.assertRaises(TypeError, lambda: zero // a)
388 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000389 self.assertRaises(ZeroDivisionError, lambda: a / zero)
390 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000391 self.assertRaises(TypeError, lambda: a / '')
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000392
393 @requires_IEEE_754
394 def test_disallowed_special(self):
395 a = timedelta(42)
396 self.assertRaises(ValueError, a.__mul__, NAN)
397 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000398
399 def test_basic_attributes(self):
400 days, seconds, us = 1, 7, 31
401 td = timedelta(days, seconds, us)
402 self.assertEqual(td.days, days)
403 self.assertEqual(td.seconds, seconds)
404 self.assertEqual(td.microseconds, us)
405
Antoine Pitroube6859d2009-11-25 23:02:32 +0000406 def test_total_seconds(self):
407 td = timedelta(days=365)
408 self.assertEqual(td.total_seconds(), 31536000.0)
409 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
410 td = timedelta(seconds=total_seconds)
411 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000412 # Issue8644: Test that td.total_seconds() has the same
413 # accuracy as td / timedelta(seconds=1).
414 for ms in [-1, -2, -123]:
415 td = timedelta(microseconds=ms)
416 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000417
Tim Peters2a799bf2002-12-16 20:18:38 +0000418 def test_carries(self):
419 t1 = timedelta(days=100,
420 weeks=-7,
421 hours=-24*(100-49),
422 minutes=-3,
423 seconds=12,
424 microseconds=(3*60 - 12) * 1e6 + 1)
425 t2 = timedelta(microseconds=1)
426 self.assertEqual(t1, t2)
427
428 def test_hash_equality(self):
429 t1 = timedelta(days=100,
430 weeks=-7,
431 hours=-24*(100-49),
432 minutes=-3,
433 seconds=12,
434 microseconds=(3*60 - 12) * 1000000)
435 t2 = timedelta()
436 self.assertEqual(hash(t1), hash(t2))
437
438 t1 += timedelta(weeks=7)
439 t2 += timedelta(days=7*7)
440 self.assertEqual(t1, t2)
441 self.assertEqual(hash(t1), hash(t2))
442
443 d = {t1: 1}
444 d[t2] = 2
445 self.assertEqual(len(d), 1)
446 self.assertEqual(d[t1], 2)
447
448 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000449 args = 12, 34, 56
450 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000451 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000452 green = pickler.dumps(orig, proto)
453 derived = unpickler.loads(green)
454 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000455
456 def test_compare(self):
457 t1 = timedelta(2, 3, 4)
458 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000459 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000460 self.assertTrue(t1 <= t2)
461 self.assertTrue(t1 >= t2)
462 self.assertTrue(not t1 != t2)
463 self.assertTrue(not t1 < t2)
464 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000465
466 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
467 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000468 self.assertTrue(t1 < t2)
469 self.assertTrue(t2 > t1)
470 self.assertTrue(t1 <= t2)
471 self.assertTrue(t2 >= t1)
472 self.assertTrue(t1 != t2)
473 self.assertTrue(t2 != t1)
474 self.assertTrue(not t1 == t2)
475 self.assertTrue(not t2 == t1)
476 self.assertTrue(not t1 > t2)
477 self.assertTrue(not t2 < t1)
478 self.assertTrue(not t1 >= t2)
479 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000480
Tim Peters68124bb2003-02-08 03:46:31 +0000481 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000482 self.assertEqual(t1 == badarg, False)
483 self.assertEqual(t1 != badarg, True)
484 self.assertEqual(badarg == t1, False)
485 self.assertEqual(badarg != t1, True)
486
Tim Peters2a799bf2002-12-16 20:18:38 +0000487 self.assertRaises(TypeError, lambda: t1 <= badarg)
488 self.assertRaises(TypeError, lambda: t1 < badarg)
489 self.assertRaises(TypeError, lambda: t1 > badarg)
490 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000491 self.assertRaises(TypeError, lambda: badarg <= t1)
492 self.assertRaises(TypeError, lambda: badarg < t1)
493 self.assertRaises(TypeError, lambda: badarg > t1)
494 self.assertRaises(TypeError, lambda: badarg >= t1)
495
496 def test_str(self):
497 td = timedelta
498 eq = self.assertEqual
499
500 eq(str(td(1)), "1 day, 0:00:00")
501 eq(str(td(-1)), "-1 day, 0:00:00")
502 eq(str(td(2)), "2 days, 0:00:00")
503 eq(str(td(-2)), "-2 days, 0:00:00")
504
505 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
506 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
507 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
508 "-210 days, 23:12:34")
509
510 eq(str(td(milliseconds=1)), "0:00:00.001000")
511 eq(str(td(microseconds=3)), "0:00:00.000003")
512
513 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
514 microseconds=999999)),
515 "999999999 days, 23:59:59.999999")
516
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000517 def test_repr(self):
518 name = 'datetime.' + self.theclass.__name__
519 self.assertEqual(repr(self.theclass(1)),
520 "%s(1)" % name)
521 self.assertEqual(repr(self.theclass(10, 2)),
522 "%s(10, 2)" % name)
523 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
524 "%s(-10, 2, 400000)" % name)
525
Tim Peters2a799bf2002-12-16 20:18:38 +0000526 def test_roundtrip(self):
527 for td in (timedelta(days=999999999, hours=23, minutes=59,
528 seconds=59, microseconds=999999),
529 timedelta(days=-999999999),
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000530 timedelta(days=-999999999, seconds=1),
Tim Peters2a799bf2002-12-16 20:18:38 +0000531 timedelta(days=1, seconds=2, microseconds=3)):
532
533 # Verify td -> string -> td identity.
534 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000535 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000536 s = s[9:]
537 td2 = eval(s)
538 self.assertEqual(td, td2)
539
540 # Verify identity via reconstructing from pieces.
541 td2 = timedelta(td.days, td.seconds, td.microseconds)
542 self.assertEqual(td, td2)
543
544 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000545 self.assertIsInstance(timedelta.min, timedelta)
546 self.assertIsInstance(timedelta.max, timedelta)
547 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000548 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000549 self.assertEqual(timedelta.min, timedelta(-999999999))
550 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
551 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
552
553 def test_overflow(self):
554 tiny = timedelta.resolution
555
556 td = timedelta.min + tiny
557 td -= tiny # no problem
558 self.assertRaises(OverflowError, td.__sub__, tiny)
559 self.assertRaises(OverflowError, td.__add__, -tiny)
560
561 td = timedelta.max - tiny
562 td += tiny # no problem
563 self.assertRaises(OverflowError, td.__add__, tiny)
564 self.assertRaises(OverflowError, td.__sub__, -tiny)
565
566 self.assertRaises(OverflowError, lambda: -timedelta.max)
567
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000568 day = timedelta(1)
569 self.assertRaises(OverflowError, day.__mul__, 10**9)
570 self.assertRaises(OverflowError, day.__mul__, 1e9)
571 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
572 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
573 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
574
575 @requires_IEEE_754
576 def _test_overflow_special(self):
577 day = timedelta(1)
578 self.assertRaises(OverflowError, day.__mul__, INF)
579 self.assertRaises(OverflowError, day.__mul__, -INF)
580
Tim Peters2a799bf2002-12-16 20:18:38 +0000581 def test_microsecond_rounding(self):
582 td = timedelta
583 eq = self.assertEqual
584
585 # Single-field rounding.
586 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
587 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
588 eq(td(milliseconds=0.6/1000), td(microseconds=1))
589 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
590
591 # Rounding due to contributions from more than one field.
592 us_per_hour = 3600e6
593 us_per_day = us_per_hour * 24
594 eq(td(days=.4/us_per_day), td(0))
595 eq(td(hours=.2/us_per_hour), td(0))
596 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
597
598 eq(td(days=-.4/us_per_day), td(0))
599 eq(td(hours=-.2/us_per_hour), td(0))
600 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
601
602 def test_massive_normalization(self):
603 td = timedelta(microseconds=-1)
604 self.assertEqual((td.days, td.seconds, td.microseconds),
605 (-1, 24*3600-1, 999999))
606
607 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000608 self.assertTrue(timedelta(1))
609 self.assertTrue(timedelta(0, 1))
610 self.assertTrue(timedelta(0, 0, 1))
611 self.assertTrue(timedelta(microseconds=1))
612 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000613
Tim Petersb0c854d2003-05-17 15:57:00 +0000614 def test_subclass_timedelta(self):
615
616 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000617 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000618 def from_td(td):
619 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000620
621 def as_hours(self):
622 sum = (self.days * 24 +
623 self.seconds / 3600.0 +
624 self.microseconds / 3600e6)
625 return round(sum)
626
627 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000628 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000629 self.assertEqual(t1.as_hours(), 24)
630
631 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000632 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000633 self.assertEqual(t2.as_hours(), -25)
634
635 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000636 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000637 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000638 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000639 self.assertEqual(t3.days, t4.days)
640 self.assertEqual(t3.seconds, t4.seconds)
641 self.assertEqual(t3.microseconds, t4.microseconds)
642 self.assertEqual(str(t3), str(t4))
643 self.assertEqual(t4.as_hours(), -1)
644
Mark Dickinson7c186e22010-04-20 22:32:49 +0000645 def test_division(self):
646 t = timedelta(hours=1, minutes=24, seconds=19)
647 second = timedelta(seconds=1)
648 self.assertEqual(t / second, 5059.0)
649 self.assertEqual(t // second, 5059)
650
651 t = timedelta(minutes=2, seconds=30)
652 minute = timedelta(minutes=1)
653 self.assertEqual(t / minute, 2.5)
654 self.assertEqual(t // minute, 2)
655
656 zerotd = timedelta(0)
657 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
658 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
659
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000660 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000661 # note: floor division of a timedelta by an integer *is*
662 # currently permitted.
663
664 def test_remainder(self):
665 t = timedelta(minutes=2, seconds=30)
666 minute = timedelta(minutes=1)
667 r = t % minute
668 self.assertEqual(r, timedelta(seconds=30))
669
670 t = timedelta(minutes=-2, seconds=30)
671 r = t % minute
672 self.assertEqual(r, timedelta(seconds=30))
673
674 zerotd = timedelta(0)
675 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
676
677 self.assertRaises(TypeError, mod, t, 10)
678
679 def test_divmod(self):
680 t = timedelta(minutes=2, seconds=30)
681 minute = timedelta(minutes=1)
682 q, r = divmod(t, minute)
683 self.assertEqual(q, 2)
684 self.assertEqual(r, timedelta(seconds=30))
685
686 t = timedelta(minutes=-2, seconds=30)
687 q, r = divmod(t, minute)
688 self.assertEqual(q, -2)
689 self.assertEqual(r, timedelta(seconds=30))
690
691 zerotd = timedelta(0)
692 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
693
694 self.assertRaises(TypeError, divmod, t, 10)
695
696
Tim Peters2a799bf2002-12-16 20:18:38 +0000697#############################################################################
698# date tests
699
700class TestDateOnly(unittest.TestCase):
701 # Tests here won't pass if also run on datetime objects, so don't
702 # subclass this to test datetimes too.
703
704 def test_delta_non_days_ignored(self):
705 dt = date(2000, 1, 2)
706 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
707 microseconds=5)
708 days = timedelta(delta.days)
709 self.assertEqual(days, timedelta(1))
710
711 dt2 = dt + delta
712 self.assertEqual(dt2, dt + days)
713
714 dt2 = delta + dt
715 self.assertEqual(dt2, dt + days)
716
717 dt2 = dt - delta
718 self.assertEqual(dt2, dt - days)
719
720 delta = -delta
721 days = timedelta(delta.days)
722 self.assertEqual(days, timedelta(-2))
723
724 dt2 = dt + delta
725 self.assertEqual(dt2, dt + days)
726
727 dt2 = delta + dt
728 self.assertEqual(dt2, dt + days)
729
730 dt2 = dt - delta
731 self.assertEqual(dt2, dt - days)
732
Tim Peters604c0132004-06-07 23:04:33 +0000733class SubclassDate(date):
734 sub_var = 1
735
Guido van Rossumd8faa362007-04-27 19:54:29 +0000736class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000737 # Tests here should pass for both dates and datetimes, except for a
738 # few tests that TestDateTime overrides.
739
740 theclass = date
741
742 def test_basic_attributes(self):
743 dt = self.theclass(2002, 3, 1)
744 self.assertEqual(dt.year, 2002)
745 self.assertEqual(dt.month, 3)
746 self.assertEqual(dt.day, 1)
747
748 def test_roundtrip(self):
749 for dt in (self.theclass(1, 2, 3),
750 self.theclass.today()):
751 # Verify dt -> string -> date identity.
752 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000753 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000754 s = s[9:]
755 dt2 = eval(s)
756 self.assertEqual(dt, dt2)
757
758 # Verify identity via reconstructing from pieces.
759 dt2 = self.theclass(dt.year, dt.month, dt.day)
760 self.assertEqual(dt, dt2)
761
762 def test_ordinal_conversions(self):
763 # Check some fixed values.
764 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
765 (1, 12, 31, 365),
766 (2, 1, 1, 366),
767 # first example from "Calendrical Calculations"
768 (1945, 11, 12, 710347)]:
769 d = self.theclass(y, m, d)
770 self.assertEqual(n, d.toordinal())
771 fromord = self.theclass.fromordinal(n)
772 self.assertEqual(d, fromord)
773 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000774 # if we're checking something fancier than a date, verify
775 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000776 self.assertEqual(fromord.hour, 0)
777 self.assertEqual(fromord.minute, 0)
778 self.assertEqual(fromord.second, 0)
779 self.assertEqual(fromord.microsecond, 0)
780
Tim Peters0bf60bd2003-01-08 20:40:01 +0000781 # Check first and last days of year spottily across the whole
782 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000783 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000784 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
785 d = self.theclass(year, 1, 1)
786 n = d.toordinal()
787 d2 = self.theclass.fromordinal(n)
788 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000789 # Verify that moving back a day gets to the end of year-1.
790 if year > 1:
791 d = self.theclass.fromordinal(n-1)
792 d2 = self.theclass(year-1, 12, 31)
793 self.assertEqual(d, d2)
794 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000795
796 # Test every day in a leap-year and a non-leap year.
797 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
798 for year, isleap in (2000, True), (2002, False):
799 n = self.theclass(year, 1, 1).toordinal()
800 for month, maxday in zip(range(1, 13), dim):
801 if month == 2 and isleap:
802 maxday += 1
803 for day in range(1, maxday+1):
804 d = self.theclass(year, month, day)
805 self.assertEqual(d.toordinal(), n)
806 self.assertEqual(d, self.theclass.fromordinal(n))
807 n += 1
808
809 def test_extreme_ordinals(self):
810 a = self.theclass.min
811 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
812 aord = a.toordinal()
813 b = a.fromordinal(aord)
814 self.assertEqual(a, b)
815
816 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
817
818 b = a + timedelta(days=1)
819 self.assertEqual(b.toordinal(), aord + 1)
820 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
821
822 a = self.theclass.max
823 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
824 aord = a.toordinal()
825 b = a.fromordinal(aord)
826 self.assertEqual(a, b)
827
828 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
829
830 b = a - timedelta(days=1)
831 self.assertEqual(b.toordinal(), aord - 1)
832 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
833
834 def test_bad_constructor_arguments(self):
835 # bad years
836 self.theclass(MINYEAR, 1, 1) # no exception
837 self.theclass(MAXYEAR, 1, 1) # no exception
838 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
839 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
840 # bad months
841 self.theclass(2000, 1, 1) # no exception
842 self.theclass(2000, 12, 1) # no exception
843 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
844 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
845 # bad days
846 self.theclass(2000, 2, 29) # no exception
847 self.theclass(2004, 2, 29) # no exception
848 self.theclass(2400, 2, 29) # no exception
849 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
850 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
851 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
852 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
853 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
854 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
855
856 def test_hash_equality(self):
857 d = self.theclass(2000, 12, 31)
858 # same thing
859 e = self.theclass(2000, 12, 31)
860 self.assertEqual(d, e)
861 self.assertEqual(hash(d), hash(e))
862
863 dic = {d: 1}
864 dic[e] = 2
865 self.assertEqual(len(dic), 1)
866 self.assertEqual(dic[d], 2)
867 self.assertEqual(dic[e], 2)
868
869 d = self.theclass(2001, 1, 1)
870 # same thing
871 e = self.theclass(2001, 1, 1)
872 self.assertEqual(d, e)
873 self.assertEqual(hash(d), hash(e))
874
875 dic = {d: 1}
876 dic[e] = 2
877 self.assertEqual(len(dic), 1)
878 self.assertEqual(dic[d], 2)
879 self.assertEqual(dic[e], 2)
880
881 def test_computations(self):
882 a = self.theclass(2002, 1, 31)
883 b = self.theclass(1956, 1, 31)
884
885 diff = a-b
886 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
887 self.assertEqual(diff.seconds, 0)
888 self.assertEqual(diff.microseconds, 0)
889
890 day = timedelta(1)
891 week = timedelta(7)
892 a = self.theclass(2002, 3, 2)
893 self.assertEqual(a + day, self.theclass(2002, 3, 3))
894 self.assertEqual(day + a, self.theclass(2002, 3, 3))
895 self.assertEqual(a - day, self.theclass(2002, 3, 1))
896 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
897 self.assertEqual(a + week, self.theclass(2002, 3, 9))
898 self.assertEqual(a - week, self.theclass(2002, 2, 23))
899 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
900 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
901 self.assertEqual((a + week) - a, week)
902 self.assertEqual((a + day) - a, day)
903 self.assertEqual((a - week) - a, -week)
904 self.assertEqual((a - day) - a, -day)
905 self.assertEqual(a - (a + week), -week)
906 self.assertEqual(a - (a + day), -day)
907 self.assertEqual(a - (a - week), week)
908 self.assertEqual(a - (a - day), day)
909
Mark Dickinson5c2db372009-12-05 20:28:34 +0000910 # Add/sub ints or floats should be illegal
911 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000912 self.assertRaises(TypeError, lambda: a+i)
913 self.assertRaises(TypeError, lambda: a-i)
914 self.assertRaises(TypeError, lambda: i+a)
915 self.assertRaises(TypeError, lambda: i-a)
916
917 # delta - date is senseless.
918 self.assertRaises(TypeError, lambda: day - a)
919 # mixing date and (delta or date) via * or // is senseless
920 self.assertRaises(TypeError, lambda: day * a)
921 self.assertRaises(TypeError, lambda: a * day)
922 self.assertRaises(TypeError, lambda: day // a)
923 self.assertRaises(TypeError, lambda: a // day)
924 self.assertRaises(TypeError, lambda: a * a)
925 self.assertRaises(TypeError, lambda: a // a)
926 # date + date is senseless
927 self.assertRaises(TypeError, lambda: a + a)
928
929 def test_overflow(self):
930 tiny = self.theclass.resolution
931
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000932 for delta in [tiny, timedelta(1), timedelta(2)]:
933 dt = self.theclass.min + delta
934 dt -= delta # no problem
935 self.assertRaises(OverflowError, dt.__sub__, delta)
936 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000937
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000938 dt = self.theclass.max - delta
939 dt += delta # no problem
940 self.assertRaises(OverflowError, dt.__add__, delta)
941 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000942
943 def test_fromtimestamp(self):
944 import time
945
946 # Try an arbitrary fixed value.
947 year, month, day = 1999, 9, 19
948 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
949 d = self.theclass.fromtimestamp(ts)
950 self.assertEqual(d.year, year)
951 self.assertEqual(d.month, month)
952 self.assertEqual(d.day, day)
953
Tim Peters1b6f7a92004-06-20 02:50:16 +0000954 def test_insane_fromtimestamp(self):
955 # It's possible that some platform maps time_t to double,
956 # and that this test will fail there. This test should
957 # exempt such platforms (provided they return reasonable
958 # results!).
959 for insane in -1e200, 1e200:
960 self.assertRaises(ValueError, self.theclass.fromtimestamp,
961 insane)
962
Tim Peters2a799bf2002-12-16 20:18:38 +0000963 def test_today(self):
964 import time
965
966 # We claim that today() is like fromtimestamp(time.time()), so
967 # prove it.
968 for dummy in range(3):
969 today = self.theclass.today()
970 ts = time.time()
971 todayagain = self.theclass.fromtimestamp(ts)
972 if today == todayagain:
973 break
974 # There are several legit reasons that could fail:
975 # 1. It recently became midnight, between the today() and the
976 # time() calls.
977 # 2. The platform time() has such fine resolution that we'll
978 # never get the same value twice.
979 # 3. The platform time() has poor resolution, and we just
980 # happened to call today() right before a resolution quantum
981 # boundary.
982 # 4. The system clock got fiddled between calls.
983 # In any case, wait a little while and try again.
984 time.sleep(0.1)
985
986 # It worked or it didn't. If it didn't, assume it's reason #2, and
987 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000988 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000989 abs(todayagain - today) < timedelta(seconds=0.5))
990
991 def test_weekday(self):
992 for i in range(7):
993 # March 4, 2002 is a Monday
994 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
995 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
996 # January 2, 1956 is a Monday
997 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
998 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
999
1000 def test_isocalendar(self):
1001 # Check examples from
1002 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1003 for i in range(7):
1004 d = self.theclass(2003, 12, 22+i)
1005 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1006 d = self.theclass(2003, 12, 29) + timedelta(i)
1007 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1008 d = self.theclass(2004, 1, 5+i)
1009 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1010 d = self.theclass(2009, 12, 21+i)
1011 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1012 d = self.theclass(2009, 12, 28) + timedelta(i)
1013 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1014 d = self.theclass(2010, 1, 4+i)
1015 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1016
1017 def test_iso_long_years(self):
1018 # Calculate long ISO years and compare to table from
1019 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1020 ISO_LONG_YEARS_TABLE = """
1021 4 32 60 88
1022 9 37 65 93
1023 15 43 71 99
1024 20 48 76
1025 26 54 82
1026
1027 105 133 161 189
1028 111 139 167 195
1029 116 144 172
1030 122 150 178
1031 128 156 184
1032
1033 201 229 257 285
1034 207 235 263 291
1035 212 240 268 296
1036 218 246 274
1037 224 252 280
1038
1039 303 331 359 387
1040 308 336 364 392
1041 314 342 370 398
1042 320 348 376
1043 325 353 381
1044 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001045 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001046 L = []
1047 for i in range(400):
1048 d = self.theclass(2000+i, 12, 31)
1049 d1 = self.theclass(1600+i, 12, 31)
1050 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1051 if d.isocalendar()[1] == 53:
1052 L.append(i)
1053 self.assertEqual(L, iso_long_years)
1054
1055 def test_isoformat(self):
1056 t = self.theclass(2, 3, 2)
1057 self.assertEqual(t.isoformat(), "0002-03-02")
1058
1059 def test_ctime(self):
1060 t = self.theclass(2002, 3, 2)
1061 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1062
1063 def test_strftime(self):
1064 t = self.theclass(2005, 3, 2)
1065 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001066 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001067 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001068
1069 self.assertRaises(TypeError, t.strftime) # needs an arg
1070 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1071 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1072
Georg Brandlf78e02b2008-06-10 17:40:04 +00001073 # test that unicode input is allowed (issue 2782)
1074 self.assertEqual(t.strftime("%m"), "03")
1075
Tim Peters2a799bf2002-12-16 20:18:38 +00001076 # A naive object replaces %z and %Z w/ empty strings.
1077 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1078
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001079 #make sure that invalid format specifiers are handled correctly
1080 #self.assertRaises(ValueError, t.strftime, "%e")
1081 #self.assertRaises(ValueError, t.strftime, "%")
1082 #self.assertRaises(ValueError, t.strftime, "%#")
1083
1084 #oh well, some systems just ignore those invalid ones.
1085 #at least, excercise them to make sure that no crashes
1086 #are generated
1087 for f in ["%e", "%", "%#"]:
1088 try:
1089 t.strftime(f)
1090 except ValueError:
1091 pass
1092
1093 #check that this standard extension works
1094 t.strftime("%f")
1095
Georg Brandlf78e02b2008-06-10 17:40:04 +00001096
Eric Smith1ba31142007-09-11 18:06:02 +00001097 def test_format(self):
1098 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001099 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001100
1101 # check that a derived class's __str__() gets called
1102 class A(self.theclass):
1103 def __str__(self):
1104 return 'A'
1105 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001106 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001107
1108 # check that a derived class's strftime gets called
1109 class B(self.theclass):
1110 def strftime(self, format_spec):
1111 return 'B'
1112 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001113 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001114
1115 for fmt in ["m:%m d:%d y:%y",
1116 "m:%m d:%d y:%y H:%H M:%M S:%S",
1117 "%z %Z",
1118 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001119 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1120 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1121 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001122
Tim Peters2a799bf2002-12-16 20:18:38 +00001123 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001124 # XXX: Should min and max respect subclassing?
1125 if issubclass(self.theclass, datetime):
1126 expected_class = datetime
1127 else:
1128 expected_class = date
1129 self.assertIsInstance(self.theclass.min, expected_class)
1130 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001131 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001132 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001133
1134 def test_extreme_timedelta(self):
1135 big = self.theclass.max - self.theclass.min
1136 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1137 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1138 # n == 315537897599999999 ~= 2**58.13
1139 justasbig = timedelta(0, 0, n)
1140 self.assertEqual(big, justasbig)
1141 self.assertEqual(self.theclass.min + big, self.theclass.max)
1142 self.assertEqual(self.theclass.max - big, self.theclass.min)
1143
1144 def test_timetuple(self):
1145 for i in range(7):
1146 # January 2, 1956 is a Monday (0)
1147 d = self.theclass(1956, 1, 2+i)
1148 t = d.timetuple()
1149 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1150 # February 1, 1956 is a Wednesday (2)
1151 d = self.theclass(1956, 2, 1+i)
1152 t = d.timetuple()
1153 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1154 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1155 # of the year.
1156 d = self.theclass(1956, 3, 1+i)
1157 t = d.timetuple()
1158 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1159 self.assertEqual(t.tm_year, 1956)
1160 self.assertEqual(t.tm_mon, 3)
1161 self.assertEqual(t.tm_mday, 1+i)
1162 self.assertEqual(t.tm_hour, 0)
1163 self.assertEqual(t.tm_min, 0)
1164 self.assertEqual(t.tm_sec, 0)
1165 self.assertEqual(t.tm_wday, (3+i)%7)
1166 self.assertEqual(t.tm_yday, 61+i)
1167 self.assertEqual(t.tm_isdst, -1)
1168
1169 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001170 args = 6, 7, 23
1171 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001172 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001173 green = pickler.dumps(orig, proto)
1174 derived = unpickler.loads(green)
1175 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001176
1177 def test_compare(self):
1178 t1 = self.theclass(2, 3, 4)
1179 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001180 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001181 self.assertTrue(t1 <= t2)
1182 self.assertTrue(t1 >= t2)
1183 self.assertTrue(not t1 != t2)
1184 self.assertTrue(not t1 < t2)
1185 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001186
1187 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1188 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001189 self.assertTrue(t1 < t2)
1190 self.assertTrue(t2 > t1)
1191 self.assertTrue(t1 <= t2)
1192 self.assertTrue(t2 >= t1)
1193 self.assertTrue(t1 != t2)
1194 self.assertTrue(t2 != t1)
1195 self.assertTrue(not t1 == t2)
1196 self.assertTrue(not t2 == t1)
1197 self.assertTrue(not t1 > t2)
1198 self.assertTrue(not t2 < t1)
1199 self.assertTrue(not t1 >= t2)
1200 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001201
Tim Peters68124bb2003-02-08 03:46:31 +00001202 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001203 self.assertEqual(t1 == badarg, False)
1204 self.assertEqual(t1 != badarg, True)
1205 self.assertEqual(badarg == t1, False)
1206 self.assertEqual(badarg != t1, True)
1207
Tim Peters2a799bf2002-12-16 20:18:38 +00001208 self.assertRaises(TypeError, lambda: t1 < badarg)
1209 self.assertRaises(TypeError, lambda: t1 > badarg)
1210 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001211 self.assertRaises(TypeError, lambda: badarg <= t1)
1212 self.assertRaises(TypeError, lambda: badarg < t1)
1213 self.assertRaises(TypeError, lambda: badarg > t1)
1214 self.assertRaises(TypeError, lambda: badarg >= t1)
1215
Tim Peters8d81a012003-01-24 22:36:34 +00001216 def test_mixed_compare(self):
1217 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001218
1219 # Our class can be compared for equality to other classes
1220 self.assertEqual(our == 1, False)
1221 self.assertEqual(1 == our, False)
1222 self.assertEqual(our != 1, True)
1223 self.assertEqual(1 != our, True)
1224
1225 # But the ordering is undefined
1226 self.assertRaises(TypeError, lambda: our < 1)
1227 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001228
Guido van Rossum19960592006-08-24 17:29:38 +00001229 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001230
Guido van Rossum19960592006-08-24 17:29:38 +00001231 class SomeClass:
1232 pass
1233
1234 their = SomeClass()
1235 self.assertEqual(our == their, False)
1236 self.assertEqual(their == our, False)
1237 self.assertEqual(our != their, True)
1238 self.assertEqual(their != our, True)
1239 self.assertRaises(TypeError, lambda: our < their)
1240 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001241
Guido van Rossum19960592006-08-24 17:29:38 +00001242 # However, if the other class explicitly defines ordering
1243 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001244
Guido van Rossum19960592006-08-24 17:29:38 +00001245 class LargerThanAnything:
1246 def __lt__(self, other):
1247 return False
1248 def __le__(self, other):
1249 return isinstance(other, LargerThanAnything)
1250 def __eq__(self, other):
1251 return isinstance(other, LargerThanAnything)
1252 def __ne__(self, other):
1253 return not isinstance(other, LargerThanAnything)
1254 def __gt__(self, other):
1255 return not isinstance(other, LargerThanAnything)
1256 def __ge__(self, other):
1257 return True
1258
1259 their = LargerThanAnything()
1260 self.assertEqual(our == their, False)
1261 self.assertEqual(their == our, False)
1262 self.assertEqual(our != their, True)
1263 self.assertEqual(their != our, True)
1264 self.assertEqual(our < their, True)
1265 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001266
Tim Peters2a799bf2002-12-16 20:18:38 +00001267 def test_bool(self):
1268 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001269 self.assertTrue(self.theclass.min)
1270 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001271
Guido van Rossum04110fb2007-08-24 16:32:05 +00001272 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001273 # For nasty technical reasons, we can't handle years before 1900.
1274 cls = self.theclass
1275 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1276 for y in 1, 49, 51, 99, 100, 1000, 1899:
1277 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001278
1279 def test_replace(self):
1280 cls = self.theclass
1281 args = [1, 2, 3]
1282 base = cls(*args)
1283 self.assertEqual(base, base.replace())
1284
1285 i = 0
1286 for name, newval in (("year", 2),
1287 ("month", 3),
1288 ("day", 4)):
1289 newargs = args[:]
1290 newargs[i] = newval
1291 expected = cls(*newargs)
1292 got = base.replace(**{name: newval})
1293 self.assertEqual(expected, got)
1294 i += 1
1295
1296 # Out of bounds.
1297 base = cls(2000, 2, 29)
1298 self.assertRaises(ValueError, base.replace, year=2001)
1299
Tim Petersa98924a2003-05-17 05:55:19 +00001300 def test_subclass_date(self):
1301
1302 class C(self.theclass):
1303 theAnswer = 42
1304
1305 def __new__(cls, *args, **kws):
1306 temp = kws.copy()
1307 extra = temp.pop('extra')
1308 result = self.theclass.__new__(cls, *args, **temp)
1309 result.extra = extra
1310 return result
1311
1312 def newmeth(self, start):
1313 return start + self.year + self.month
1314
1315 args = 2003, 4, 14
1316
1317 dt1 = self.theclass(*args)
1318 dt2 = C(*args, **{'extra': 7})
1319
1320 self.assertEqual(dt2.__class__, C)
1321 self.assertEqual(dt2.theAnswer, 42)
1322 self.assertEqual(dt2.extra, 7)
1323 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1324 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1325
Tim Peters604c0132004-06-07 23:04:33 +00001326 def test_pickling_subclass_date(self):
1327
1328 args = 6, 7, 23
1329 orig = SubclassDate(*args)
1330 for pickler, unpickler, proto in pickle_choices:
1331 green = pickler.dumps(orig, proto)
1332 derived = unpickler.loads(green)
1333 self.assertEqual(orig, derived)
1334
Tim Peters3f606292004-03-21 23:38:41 +00001335 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001336 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001337 # This is a low-overhead backdoor. A user can (by intent or
1338 # mistake) pass a string directly, which (if it's the right length)
1339 # will get treated like a pickle, and bypass the normal sanity
1340 # checks in the constructor. This can create insane objects.
1341 # The constructor doesn't want to burn the time to validate all
1342 # fields, but does check the month field. This stops, e.g.,
1343 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001344 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001345 if not issubclass(self.theclass, datetime):
1346 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001347 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001348 self.assertRaises(TypeError, self.theclass,
1349 base[:2] + month_byte + base[3:])
1350 for ord_byte in range(1, 13):
1351 # This shouldn't blow up because of the month byte alone. If
1352 # the implementation changes to do more-careful checking, it may
1353 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001354 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001355
Tim Peters2a799bf2002-12-16 20:18:38 +00001356#############################################################################
1357# datetime tests
1358
Tim Peters604c0132004-06-07 23:04:33 +00001359class SubclassDatetime(datetime):
1360 sub_var = 1
1361
Tim Peters2a799bf2002-12-16 20:18:38 +00001362class TestDateTime(TestDate):
1363
1364 theclass = datetime
1365
1366 def test_basic_attributes(self):
1367 dt = self.theclass(2002, 3, 1, 12, 0)
1368 self.assertEqual(dt.year, 2002)
1369 self.assertEqual(dt.month, 3)
1370 self.assertEqual(dt.day, 1)
1371 self.assertEqual(dt.hour, 12)
1372 self.assertEqual(dt.minute, 0)
1373 self.assertEqual(dt.second, 0)
1374 self.assertEqual(dt.microsecond, 0)
1375
1376 def test_basic_attributes_nonzero(self):
1377 # Make sure all attributes are non-zero so bugs in
1378 # bit-shifting access show up.
1379 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1380 self.assertEqual(dt.year, 2002)
1381 self.assertEqual(dt.month, 3)
1382 self.assertEqual(dt.day, 1)
1383 self.assertEqual(dt.hour, 12)
1384 self.assertEqual(dt.minute, 59)
1385 self.assertEqual(dt.second, 59)
1386 self.assertEqual(dt.microsecond, 8000)
1387
1388 def test_roundtrip(self):
1389 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1390 self.theclass.now()):
1391 # Verify dt -> string -> datetime identity.
1392 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001393 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001394 s = s[9:]
1395 dt2 = eval(s)
1396 self.assertEqual(dt, dt2)
1397
1398 # Verify identity via reconstructing from pieces.
1399 dt2 = self.theclass(dt.year, dt.month, dt.day,
1400 dt.hour, dt.minute, dt.second,
1401 dt.microsecond)
1402 self.assertEqual(dt, dt2)
1403
1404 def test_isoformat(self):
1405 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1406 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1407 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1408 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001409 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001410 # str is ISO format with the separator forced to a blank.
1411 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1412
1413 t = self.theclass(2, 3, 2)
1414 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1415 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1416 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1417 # str is ISO format with the separator forced to a blank.
1418 self.assertEqual(str(t), "0002-03-02 00:00:00")
1419
Eric Smith1ba31142007-09-11 18:06:02 +00001420 def test_format(self):
1421 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001422 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001423
1424 # check that a derived class's __str__() gets called
1425 class A(self.theclass):
1426 def __str__(self):
1427 return 'A'
1428 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001429 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001430
1431 # check that a derived class's strftime gets called
1432 class B(self.theclass):
1433 def strftime(self, format_spec):
1434 return 'B'
1435 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001436 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001437
1438 for fmt in ["m:%m d:%d y:%y",
1439 "m:%m d:%d y:%y H:%H M:%M S:%S",
1440 "%z %Z",
1441 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001442 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1443 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1444 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001445
Tim Peters2a799bf2002-12-16 20:18:38 +00001446 def test_more_ctime(self):
1447 # Test fields that TestDate doesn't touch.
1448 import time
1449
1450 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1451 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1452 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1453 # out. The difference is that t.ctime() produces " 2" for the day,
1454 # but platform ctime() produces "02" for the day. According to
1455 # C99, t.ctime() is correct here.
1456 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1457
1458 # So test a case where that difference doesn't matter.
1459 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1460 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1461
1462 def test_tz_independent_comparing(self):
1463 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1464 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1465 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1466 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001467 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001468
1469 # Make sure comparison doesn't forget microseconds, and isn't done
1470 # via comparing a float timestamp (an IEEE double doesn't have enough
1471 # precision to span microsecond resolution across years 1 thru 9999,
1472 # so comparing via timestamp necessarily calls some distinct values
1473 # equal).
1474 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1475 us = timedelta(microseconds=1)
1476 dt2 = dt1 + us
1477 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001478 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001479
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001480 def test_strftime_with_bad_tzname_replace(self):
1481 # verify ok if tzinfo.tzname().replace() returns a non-string
1482 class MyTzInfo(FixedOffset):
1483 def tzname(self, dt):
1484 class MyStr(str):
1485 def replace(self, *args):
1486 return None
1487 return MyStr('name')
1488 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1489 self.assertRaises(TypeError, t.strftime, '%Z')
1490
Tim Peters2a799bf2002-12-16 20:18:38 +00001491 def test_bad_constructor_arguments(self):
1492 # bad years
1493 self.theclass(MINYEAR, 1, 1) # no exception
1494 self.theclass(MAXYEAR, 1, 1) # no exception
1495 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1496 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1497 # bad months
1498 self.theclass(2000, 1, 1) # no exception
1499 self.theclass(2000, 12, 1) # no exception
1500 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1501 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1502 # bad days
1503 self.theclass(2000, 2, 29) # no exception
1504 self.theclass(2004, 2, 29) # no exception
1505 self.theclass(2400, 2, 29) # no exception
1506 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1507 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1508 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1509 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1510 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1511 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1512 # bad hours
1513 self.theclass(2000, 1, 31, 0) # no exception
1514 self.theclass(2000, 1, 31, 23) # no exception
1515 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1516 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1517 # bad minutes
1518 self.theclass(2000, 1, 31, 23, 0) # no exception
1519 self.theclass(2000, 1, 31, 23, 59) # no exception
1520 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1521 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1522 # bad seconds
1523 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1524 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1525 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1526 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1527 # bad microseconds
1528 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1529 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1530 self.assertRaises(ValueError, self.theclass,
1531 2000, 1, 31, 23, 59, 59, -1)
1532 self.assertRaises(ValueError, self.theclass,
1533 2000, 1, 31, 23, 59, 59,
1534 1000000)
1535
1536 def test_hash_equality(self):
1537 d = self.theclass(2000, 12, 31, 23, 30, 17)
1538 e = self.theclass(2000, 12, 31, 23, 30, 17)
1539 self.assertEqual(d, e)
1540 self.assertEqual(hash(d), hash(e))
1541
1542 dic = {d: 1}
1543 dic[e] = 2
1544 self.assertEqual(len(dic), 1)
1545 self.assertEqual(dic[d], 2)
1546 self.assertEqual(dic[e], 2)
1547
1548 d = self.theclass(2001, 1, 1, 0, 5, 17)
1549 e = self.theclass(2001, 1, 1, 0, 5, 17)
1550 self.assertEqual(d, e)
1551 self.assertEqual(hash(d), hash(e))
1552
1553 dic = {d: 1}
1554 dic[e] = 2
1555 self.assertEqual(len(dic), 1)
1556 self.assertEqual(dic[d], 2)
1557 self.assertEqual(dic[e], 2)
1558
1559 def test_computations(self):
1560 a = self.theclass(2002, 1, 31)
1561 b = self.theclass(1956, 1, 31)
1562 diff = a-b
1563 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1564 self.assertEqual(diff.seconds, 0)
1565 self.assertEqual(diff.microseconds, 0)
1566 a = self.theclass(2002, 3, 2, 17, 6)
1567 millisec = timedelta(0, 0, 1000)
1568 hour = timedelta(0, 3600)
1569 day = timedelta(1)
1570 week = timedelta(7)
1571 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1572 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1573 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1574 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1575 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1576 self.assertEqual(a - hour, a + -hour)
1577 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1578 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1579 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1580 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1581 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1582 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1583 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1584 self.assertEqual((a + week) - a, week)
1585 self.assertEqual((a + day) - a, day)
1586 self.assertEqual((a + hour) - a, hour)
1587 self.assertEqual((a + millisec) - a, millisec)
1588 self.assertEqual((a - week) - a, -week)
1589 self.assertEqual((a - day) - a, -day)
1590 self.assertEqual((a - hour) - a, -hour)
1591 self.assertEqual((a - millisec) - a, -millisec)
1592 self.assertEqual(a - (a + week), -week)
1593 self.assertEqual(a - (a + day), -day)
1594 self.assertEqual(a - (a + hour), -hour)
1595 self.assertEqual(a - (a + millisec), -millisec)
1596 self.assertEqual(a - (a - week), week)
1597 self.assertEqual(a - (a - day), day)
1598 self.assertEqual(a - (a - hour), hour)
1599 self.assertEqual(a - (a - millisec), millisec)
1600 self.assertEqual(a + (week + day + hour + millisec),
1601 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1602 self.assertEqual(a + (week + day + hour + millisec),
1603 (((a + week) + day) + hour) + millisec)
1604 self.assertEqual(a - (week + day + hour + millisec),
1605 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1606 self.assertEqual(a - (week + day + hour + millisec),
1607 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001608 # Add/sub ints or floats should be illegal
1609 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001610 self.assertRaises(TypeError, lambda: a+i)
1611 self.assertRaises(TypeError, lambda: a-i)
1612 self.assertRaises(TypeError, lambda: i+a)
1613 self.assertRaises(TypeError, lambda: i-a)
1614
1615 # delta - datetime is senseless.
1616 self.assertRaises(TypeError, lambda: day - a)
1617 # mixing datetime and (delta or datetime) via * or // is senseless
1618 self.assertRaises(TypeError, lambda: day * a)
1619 self.assertRaises(TypeError, lambda: a * day)
1620 self.assertRaises(TypeError, lambda: day // a)
1621 self.assertRaises(TypeError, lambda: a // day)
1622 self.assertRaises(TypeError, lambda: a * a)
1623 self.assertRaises(TypeError, lambda: a // a)
1624 # datetime + datetime is senseless
1625 self.assertRaises(TypeError, lambda: a + a)
1626
1627 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001628 args = 6, 7, 23, 20, 59, 1, 64**2
1629 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001630 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001631 green = pickler.dumps(orig, proto)
1632 derived = unpickler.loads(green)
1633 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001634
Guido van Rossum275666f2003-02-07 21:49:01 +00001635 def test_more_pickling(self):
1636 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1637 s = pickle.dumps(a)
1638 b = pickle.loads(s)
1639 self.assertEqual(b.year, 2003)
1640 self.assertEqual(b.month, 2)
1641 self.assertEqual(b.day, 7)
1642
Tim Peters604c0132004-06-07 23:04:33 +00001643 def test_pickling_subclass_datetime(self):
1644 args = 6, 7, 23, 20, 59, 1, 64**2
1645 orig = SubclassDatetime(*args)
1646 for pickler, unpickler, proto in pickle_choices:
1647 green = pickler.dumps(orig, proto)
1648 derived = unpickler.loads(green)
1649 self.assertEqual(orig, derived)
1650
Tim Peters2a799bf2002-12-16 20:18:38 +00001651 def test_more_compare(self):
1652 # The test_compare() inherited from TestDate covers the error cases.
1653 # We just want to test lexicographic ordering on the members datetime
1654 # has that date lacks.
1655 args = [2000, 11, 29, 20, 58, 16, 999998]
1656 t1 = self.theclass(*args)
1657 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001658 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001659 self.assertTrue(t1 <= t2)
1660 self.assertTrue(t1 >= t2)
1661 self.assertTrue(not t1 != t2)
1662 self.assertTrue(not t1 < t2)
1663 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001664
1665 for i in range(len(args)):
1666 newargs = args[:]
1667 newargs[i] = args[i] + 1
1668 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001669 self.assertTrue(t1 < t2)
1670 self.assertTrue(t2 > t1)
1671 self.assertTrue(t1 <= t2)
1672 self.assertTrue(t2 >= t1)
1673 self.assertTrue(t1 != t2)
1674 self.assertTrue(t2 != t1)
1675 self.assertTrue(not t1 == t2)
1676 self.assertTrue(not t2 == t1)
1677 self.assertTrue(not t1 > t2)
1678 self.assertTrue(not t2 < t1)
1679 self.assertTrue(not t1 >= t2)
1680 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001681
1682
1683 # A helper for timestamp constructor tests.
1684 def verify_field_equality(self, expected, got):
1685 self.assertEqual(expected.tm_year, got.year)
1686 self.assertEqual(expected.tm_mon, got.month)
1687 self.assertEqual(expected.tm_mday, got.day)
1688 self.assertEqual(expected.tm_hour, got.hour)
1689 self.assertEqual(expected.tm_min, got.minute)
1690 self.assertEqual(expected.tm_sec, got.second)
1691
1692 def test_fromtimestamp(self):
1693 import time
1694
1695 ts = time.time()
1696 expected = time.localtime(ts)
1697 got = self.theclass.fromtimestamp(ts)
1698 self.verify_field_equality(expected, got)
1699
1700 def test_utcfromtimestamp(self):
1701 import time
1702
1703 ts = time.time()
1704 expected = time.gmtime(ts)
1705 got = self.theclass.utcfromtimestamp(ts)
1706 self.verify_field_equality(expected, got)
1707
Thomas Wouters477c8d52006-05-27 19:21:47 +00001708 def test_microsecond_rounding(self):
1709 # Test whether fromtimestamp "rounds up" floats that are less
1710 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001711 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1712 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001713
Tim Peters1b6f7a92004-06-20 02:50:16 +00001714 def test_insane_fromtimestamp(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.fromtimestamp,
1721 insane)
1722
1723 def test_insane_utcfromtimestamp(self):
1724 # It's possible that some platform maps time_t to double,
1725 # and that this test will fail there. This test should
1726 # exempt such platforms (provided they return reasonable
1727 # results!).
1728 for insane in -1e200, 1e200:
1729 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1730 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001731 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001732 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001733 # The result is tz-dependent; at least test that this doesn't
1734 # fail (like it did before bug 1646728 was fixed).
1735 self.theclass.fromtimestamp(-1.05)
1736
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001737 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001738 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001739 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001740 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001741
Tim Peters2a799bf2002-12-16 20:18:38 +00001742 def test_utcnow(self):
1743 import time
1744
1745 # Call it a success if utcnow() and utcfromtimestamp() are within
1746 # a second of each other.
1747 tolerance = timedelta(seconds=1)
1748 for dummy in range(3):
1749 from_now = self.theclass.utcnow()
1750 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1751 if abs(from_timestamp - from_now) <= tolerance:
1752 break
1753 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001754 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001755
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001756 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001757 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001758
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001759 string = '2004-12-01 13:02:47.197'
1760 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001761 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001762 got = self.theclass.strptime(string, format)
1763 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001764 self.assertIs(type(expected), self.theclass)
1765 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001766
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001767 strptime = self.theclass.strptime
1768 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1769 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1770 # Only local timezone and UTC are supported
1771 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1772 (-_time.timezone, _time.tzname[0])):
1773 if tzseconds < 0:
1774 sign = '-'
1775 seconds = -tzseconds
1776 else:
1777 sign ='+'
1778 seconds = tzseconds
1779 hours, minutes = divmod(seconds//60, 60)
1780 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1781 dt = strptime(dtstr, "%z %Z")
1782 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1783 self.assertEqual(dt.tzname(), tzname)
1784 # Can produce inconsistent datetime
1785 dtstr, fmt = "+1234 UTC", "%z %Z"
1786 dt = strptime(dtstr, fmt)
1787 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1788 self.assertEqual(dt.tzname(), 'UTC')
1789 # yet will roundtrip
1790 self.assertEqual(dt.strftime(fmt), dtstr)
1791
1792 # Produce naive datetime if no %z is provided
1793 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1794
1795 with self.assertRaises(ValueError): strptime("-2400", "%z")
1796 with self.assertRaises(ValueError): strptime("-000", "%z")
1797
Tim Peters2a799bf2002-12-16 20:18:38 +00001798 def test_more_timetuple(self):
1799 # This tests fields beyond those tested by the TestDate.test_timetuple.
1800 t = self.theclass(2004, 12, 31, 6, 22, 33)
1801 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1802 self.assertEqual(t.timetuple(),
1803 (t.year, t.month, t.day,
1804 t.hour, t.minute, t.second,
1805 t.weekday(),
1806 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1807 -1))
1808 tt = t.timetuple()
1809 self.assertEqual(tt.tm_year, t.year)
1810 self.assertEqual(tt.tm_mon, t.month)
1811 self.assertEqual(tt.tm_mday, t.day)
1812 self.assertEqual(tt.tm_hour, t.hour)
1813 self.assertEqual(tt.tm_min, t.minute)
1814 self.assertEqual(tt.tm_sec, t.second)
1815 self.assertEqual(tt.tm_wday, t.weekday())
1816 self.assertEqual(tt.tm_yday, t.toordinal() -
1817 date(t.year, 1, 1).toordinal() + 1)
1818 self.assertEqual(tt.tm_isdst, -1)
1819
1820 def test_more_strftime(self):
1821 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001822 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1823 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1824 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001825
1826 def test_extract(self):
1827 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1828 self.assertEqual(dt.date(), date(2002, 3, 4))
1829 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1830
1831 def test_combine(self):
1832 d = date(2002, 3, 4)
1833 t = time(18, 45, 3, 1234)
1834 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1835 combine = self.theclass.combine
1836 dt = combine(d, t)
1837 self.assertEqual(dt, expected)
1838
1839 dt = combine(time=t, date=d)
1840 self.assertEqual(dt, expected)
1841
1842 self.assertEqual(d, dt.date())
1843 self.assertEqual(t, dt.time())
1844 self.assertEqual(dt, combine(dt.date(), dt.time()))
1845
1846 self.assertRaises(TypeError, combine) # need an arg
1847 self.assertRaises(TypeError, combine, d) # need two args
1848 self.assertRaises(TypeError, combine, t, d) # args reversed
1849 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1850 self.assertRaises(TypeError, combine, "date", "time") # wrong types
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00001851 self.assertRaises(TypeError, combine, d, "time") # wrong type
1852 self.assertRaises(TypeError, combine, "date", t) # wrong type
Tim Peters2a799bf2002-12-16 20:18:38 +00001853
Tim Peters12bf3392002-12-24 05:41:27 +00001854 def test_replace(self):
1855 cls = self.theclass
1856 args = [1, 2, 3, 4, 5, 6, 7]
1857 base = cls(*args)
1858 self.assertEqual(base, base.replace())
1859
1860 i = 0
1861 for name, newval in (("year", 2),
1862 ("month", 3),
1863 ("day", 4),
1864 ("hour", 5),
1865 ("minute", 6),
1866 ("second", 7),
1867 ("microsecond", 8)):
1868 newargs = args[:]
1869 newargs[i] = newval
1870 expected = cls(*newargs)
1871 got = base.replace(**{name: newval})
1872 self.assertEqual(expected, got)
1873 i += 1
1874
1875 # Out of bounds.
1876 base = cls(2000, 2, 29)
1877 self.assertRaises(ValueError, base.replace, year=2001)
1878
Tim Peters80475bb2002-12-25 07:40:55 +00001879 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001880 # Pretty boring! The TZ test is more interesting here. astimezone()
1881 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001882 dt = self.theclass.now()
1883 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001884 self.assertRaises(TypeError, dt.astimezone) # not enough args
1885 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1886 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001887 self.assertRaises(ValueError, dt.astimezone, f) # naive
1888 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001889
Tim Peters52dcce22003-01-23 16:36:11 +00001890 class Bogus(tzinfo):
1891 def utcoffset(self, dt): return None
1892 def dst(self, dt): return timedelta(0)
1893 bog = Bogus()
1894 self.assertRaises(ValueError, dt.astimezone, bog) # naive
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00001895 self.assertRaises(ValueError,
1896 dt.replace(tzinfo=bog).astimezone, f)
Tim Peters52dcce22003-01-23 16:36:11 +00001897
1898 class AlsoBogus(tzinfo):
1899 def utcoffset(self, dt): return timedelta(0)
1900 def dst(self, dt): return None
1901 alsobog = AlsoBogus()
1902 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001903
Tim Petersa98924a2003-05-17 05:55:19 +00001904 def test_subclass_datetime(self):
1905
1906 class C(self.theclass):
1907 theAnswer = 42
1908
1909 def __new__(cls, *args, **kws):
1910 temp = kws.copy()
1911 extra = temp.pop('extra')
1912 result = self.theclass.__new__(cls, *args, **temp)
1913 result.extra = extra
1914 return result
1915
1916 def newmeth(self, start):
1917 return start + self.year + self.month + self.second
1918
1919 args = 2003, 4, 14, 12, 13, 41
1920
1921 dt1 = self.theclass(*args)
1922 dt2 = C(*args, **{'extra': 7})
1923
1924 self.assertEqual(dt2.__class__, C)
1925 self.assertEqual(dt2.theAnswer, 42)
1926 self.assertEqual(dt2.extra, 7)
1927 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1928 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1929 dt1.second - 7)
1930
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001931class TestSubclassDateTime(TestDateTime):
1932 theclass = SubclassDatetime
1933 # Override tests not designed for subclass
1934 def test_roundtrip(self):
1935 pass
1936
Tim Peters604c0132004-06-07 23:04:33 +00001937class SubclassTime(time):
1938 sub_var = 1
1939
Guido van Rossumd8faa362007-04-27 19:54:29 +00001940class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001941
1942 theclass = time
1943
1944 def test_basic_attributes(self):
1945 t = self.theclass(12, 0)
1946 self.assertEqual(t.hour, 12)
1947 self.assertEqual(t.minute, 0)
1948 self.assertEqual(t.second, 0)
1949 self.assertEqual(t.microsecond, 0)
1950
1951 def test_basic_attributes_nonzero(self):
1952 # Make sure all attributes are non-zero so bugs in
1953 # bit-shifting access show up.
1954 t = self.theclass(12, 59, 59, 8000)
1955 self.assertEqual(t.hour, 12)
1956 self.assertEqual(t.minute, 59)
1957 self.assertEqual(t.second, 59)
1958 self.assertEqual(t.microsecond, 8000)
1959
1960 def test_roundtrip(self):
1961 t = self.theclass(1, 2, 3, 4)
1962
1963 # Verify t -> string -> time identity.
1964 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001965 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001966 s = s[9:]
1967 t2 = eval(s)
1968 self.assertEqual(t, t2)
1969
1970 # Verify identity via reconstructing from pieces.
1971 t2 = self.theclass(t.hour, t.minute, t.second,
1972 t.microsecond)
1973 self.assertEqual(t, t2)
1974
1975 def test_comparing(self):
1976 args = [1, 2, 3, 4]
1977 t1 = self.theclass(*args)
1978 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001979 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001980 self.assertTrue(t1 <= t2)
1981 self.assertTrue(t1 >= t2)
1982 self.assertTrue(not t1 != t2)
1983 self.assertTrue(not t1 < t2)
1984 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001985
1986 for i in range(len(args)):
1987 newargs = args[:]
1988 newargs[i] = args[i] + 1
1989 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001990 self.assertTrue(t1 < t2)
1991 self.assertTrue(t2 > t1)
1992 self.assertTrue(t1 <= t2)
1993 self.assertTrue(t2 >= t1)
1994 self.assertTrue(t1 != t2)
1995 self.assertTrue(t2 != t1)
1996 self.assertTrue(not t1 == t2)
1997 self.assertTrue(not t2 == t1)
1998 self.assertTrue(not t1 > t2)
1999 self.assertTrue(not t2 < t1)
2000 self.assertTrue(not t1 >= t2)
2001 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002002
Tim Peters68124bb2003-02-08 03:46:31 +00002003 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00002004 self.assertEqual(t1 == badarg, False)
2005 self.assertEqual(t1 != badarg, True)
2006 self.assertEqual(badarg == t1, False)
2007 self.assertEqual(badarg != t1, True)
2008
Tim Peters2a799bf2002-12-16 20:18:38 +00002009 self.assertRaises(TypeError, lambda: t1 <= badarg)
2010 self.assertRaises(TypeError, lambda: t1 < badarg)
2011 self.assertRaises(TypeError, lambda: t1 > badarg)
2012 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00002013 self.assertRaises(TypeError, lambda: badarg <= t1)
2014 self.assertRaises(TypeError, lambda: badarg < t1)
2015 self.assertRaises(TypeError, lambda: badarg > t1)
2016 self.assertRaises(TypeError, lambda: badarg >= t1)
2017
2018 def test_bad_constructor_arguments(self):
2019 # bad hours
2020 self.theclass(0, 0) # no exception
2021 self.theclass(23, 0) # no exception
2022 self.assertRaises(ValueError, self.theclass, -1, 0)
2023 self.assertRaises(ValueError, self.theclass, 24, 0)
2024 # bad minutes
2025 self.theclass(23, 0) # no exception
2026 self.theclass(23, 59) # no exception
2027 self.assertRaises(ValueError, self.theclass, 23, -1)
2028 self.assertRaises(ValueError, self.theclass, 23, 60)
2029 # bad seconds
2030 self.theclass(23, 59, 0) # no exception
2031 self.theclass(23, 59, 59) # no exception
2032 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2033 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2034 # bad microseconds
2035 self.theclass(23, 59, 59, 0) # no exception
2036 self.theclass(23, 59, 59, 999999) # no exception
2037 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2038 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2039
2040 def test_hash_equality(self):
2041 d = self.theclass(23, 30, 17)
2042 e = self.theclass(23, 30, 17)
2043 self.assertEqual(d, e)
2044 self.assertEqual(hash(d), hash(e))
2045
2046 dic = {d: 1}
2047 dic[e] = 2
2048 self.assertEqual(len(dic), 1)
2049 self.assertEqual(dic[d], 2)
2050 self.assertEqual(dic[e], 2)
2051
2052 d = self.theclass(0, 5, 17)
2053 e = self.theclass(0, 5, 17)
2054 self.assertEqual(d, e)
2055 self.assertEqual(hash(d), hash(e))
2056
2057 dic = {d: 1}
2058 dic[e] = 2
2059 self.assertEqual(len(dic), 1)
2060 self.assertEqual(dic[d], 2)
2061 self.assertEqual(dic[e], 2)
2062
2063 def test_isoformat(self):
2064 t = self.theclass(4, 5, 1, 123)
2065 self.assertEqual(t.isoformat(), "04:05:01.000123")
2066 self.assertEqual(t.isoformat(), str(t))
2067
2068 t = self.theclass()
2069 self.assertEqual(t.isoformat(), "00:00:00")
2070 self.assertEqual(t.isoformat(), str(t))
2071
2072 t = self.theclass(microsecond=1)
2073 self.assertEqual(t.isoformat(), "00:00:00.000001")
2074 self.assertEqual(t.isoformat(), str(t))
2075
2076 t = self.theclass(microsecond=10)
2077 self.assertEqual(t.isoformat(), "00:00:00.000010")
2078 self.assertEqual(t.isoformat(), str(t))
2079
2080 t = self.theclass(microsecond=100)
2081 self.assertEqual(t.isoformat(), "00:00:00.000100")
2082 self.assertEqual(t.isoformat(), str(t))
2083
2084 t = self.theclass(microsecond=1000)
2085 self.assertEqual(t.isoformat(), "00:00:00.001000")
2086 self.assertEqual(t.isoformat(), str(t))
2087
2088 t = self.theclass(microsecond=10000)
2089 self.assertEqual(t.isoformat(), "00:00:00.010000")
2090 self.assertEqual(t.isoformat(), str(t))
2091
2092 t = self.theclass(microsecond=100000)
2093 self.assertEqual(t.isoformat(), "00:00:00.100000")
2094 self.assertEqual(t.isoformat(), str(t))
2095
Thomas Wouterscf297e42007-02-23 15:07:44 +00002096 def test_1653736(self):
2097 # verify it doesn't accept extra keyword arguments
2098 t = self.theclass(second=1)
2099 self.assertRaises(TypeError, t.isoformat, foo=3)
2100
Tim Peters2a799bf2002-12-16 20:18:38 +00002101 def test_strftime(self):
2102 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002103 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002104 # A naive object replaces %z and %Z with empty strings.
2105 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2106
Eric Smith1ba31142007-09-11 18:06:02 +00002107 def test_format(self):
2108 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002109 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002110
2111 # check that a derived class's __str__() gets called
2112 class A(self.theclass):
2113 def __str__(self):
2114 return 'A'
2115 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002116 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002117
2118 # check that a derived class's strftime gets called
2119 class B(self.theclass):
2120 def strftime(self, format_spec):
2121 return 'B'
2122 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002123 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002124
2125 for fmt in ['%H %M %S',
2126 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002127 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2128 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2129 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002130
Tim Peters2a799bf2002-12-16 20:18:38 +00002131 def test_str(self):
2132 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2133 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2134 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2135 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2136 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2137
2138 def test_repr(self):
2139 name = 'datetime.' + self.theclass.__name__
2140 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2141 "%s(1, 2, 3, 4)" % name)
2142 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2143 "%s(10, 2, 3, 4000)" % name)
2144 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2145 "%s(0, 2, 3, 400000)" % name)
2146 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2147 "%s(12, 2, 3)" % name)
2148 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2149 "%s(23, 15)" % name)
2150
2151 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002152 self.assertIsInstance(self.theclass.min, self.theclass)
2153 self.assertIsInstance(self.theclass.max, self.theclass)
2154 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002155 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002156
2157 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002158 args = 20, 59, 16, 64**2
2159 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002160 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002161 green = pickler.dumps(orig, proto)
2162 derived = unpickler.loads(green)
2163 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002164
Tim Peters604c0132004-06-07 23:04:33 +00002165 def test_pickling_subclass_time(self):
2166 args = 20, 59, 16, 64**2
2167 orig = SubclassTime(*args)
2168 for pickler, unpickler, proto in pickle_choices:
2169 green = pickler.dumps(orig, proto)
2170 derived = unpickler.loads(green)
2171 self.assertEqual(orig, derived)
2172
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 def test_bool(self):
2174 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002175 self.assertTrue(cls(1))
2176 self.assertTrue(cls(0, 1))
2177 self.assertTrue(cls(0, 0, 1))
2178 self.assertTrue(cls(0, 0, 0, 1))
2179 self.assertTrue(not cls(0))
2180 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002181
Tim Peters12bf3392002-12-24 05:41:27 +00002182 def test_replace(self):
2183 cls = self.theclass
2184 args = [1, 2, 3, 4]
2185 base = cls(*args)
2186 self.assertEqual(base, base.replace())
2187
2188 i = 0
2189 for name, newval in (("hour", 5),
2190 ("minute", 6),
2191 ("second", 7),
2192 ("microsecond", 8)):
2193 newargs = args[:]
2194 newargs[i] = newval
2195 expected = cls(*newargs)
2196 got = base.replace(**{name: newval})
2197 self.assertEqual(expected, got)
2198 i += 1
2199
2200 # Out of bounds.
2201 base = cls(1)
2202 self.assertRaises(ValueError, base.replace, hour=24)
2203 self.assertRaises(ValueError, base.replace, minute=-1)
2204 self.assertRaises(ValueError, base.replace, second=100)
2205 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2206
Tim Petersa98924a2003-05-17 05:55:19 +00002207 def test_subclass_time(self):
2208
2209 class C(self.theclass):
2210 theAnswer = 42
2211
2212 def __new__(cls, *args, **kws):
2213 temp = kws.copy()
2214 extra = temp.pop('extra')
2215 result = self.theclass.__new__(cls, *args, **temp)
2216 result.extra = extra
2217 return result
2218
2219 def newmeth(self, start):
2220 return start + self.hour + self.second
2221
2222 args = 4, 5, 6
2223
2224 dt1 = self.theclass(*args)
2225 dt2 = C(*args, **{'extra': 7})
2226
2227 self.assertEqual(dt2.__class__, C)
2228 self.assertEqual(dt2.theAnswer, 42)
2229 self.assertEqual(dt2.extra, 7)
2230 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2231 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2232
Armin Rigof4afb212005-11-07 07:15:48 +00002233 def test_backdoor_resistance(self):
2234 # see TestDate.test_backdoor_resistance().
2235 base = '2:59.0'
2236 for hour_byte in ' ', '9', chr(24), '\xff':
2237 self.assertRaises(TypeError, self.theclass,
2238 hour_byte + base[1:])
2239
Tim Peters855fe882002-12-22 03:43:39 +00002240# A mixin for classes with a tzinfo= argument. Subclasses must define
2241# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002242# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002243class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002244
Tim Petersbad8ff02002-12-30 20:52:32 +00002245 def test_argument_passing(self):
2246 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002247 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002248 class introspective(tzinfo):
2249 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002250 def utcoffset(self, dt):
2251 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002252 dst = utcoffset
2253
2254 obj = cls(1, 2, 3, tzinfo=introspective())
2255
Tim Peters0bf60bd2003-01-08 20:40:01 +00002256 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002257 self.assertEqual(obj.tzname(), expected)
2258
Tim Peters0bf60bd2003-01-08 20:40:01 +00002259 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002260 self.assertEqual(obj.utcoffset(), expected)
2261 self.assertEqual(obj.dst(), expected)
2262
Tim Peters855fe882002-12-22 03:43:39 +00002263 def test_bad_tzinfo_classes(self):
2264 cls = self.theclass
2265 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002266
Tim Peters855fe882002-12-22 03:43:39 +00002267 class NiceTry(object):
2268 def __init__(self): pass
2269 def utcoffset(self, dt): pass
2270 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2271
2272 class BetterTry(tzinfo):
2273 def __init__(self): pass
2274 def utcoffset(self, dt): pass
2275 b = BetterTry()
2276 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002277 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002278
2279 def test_utc_offset_out_of_bounds(self):
2280 class Edgy(tzinfo):
2281 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002282 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002283 def utcoffset(self, dt):
2284 return self.offset
2285
2286 cls = self.theclass
2287 for offset, legit in ((-1440, False),
2288 (-1439, True),
2289 (1439, True),
2290 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002291 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002292 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002293 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002294 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002295 else:
2296 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002297 if legit:
2298 aofs = abs(offset)
2299 h, m = divmod(aofs, 60)
2300 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002301 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002302 t = t.timetz()
2303 self.assertEqual(str(t), "01:02:03" + tag)
2304 else:
2305 self.assertRaises(ValueError, str, t)
2306
2307 def test_tzinfo_classes(self):
2308 cls = self.theclass
2309 class C1(tzinfo):
2310 def utcoffset(self, dt): return None
2311 def dst(self, dt): return None
2312 def tzname(self, dt): return None
2313 for t in (cls(1, 1, 1),
2314 cls(1, 1, 1, tzinfo=None),
2315 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002316 self.assertTrue(t.utcoffset() is None)
2317 self.assertTrue(t.dst() is None)
2318 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002319
Tim Peters855fe882002-12-22 03:43:39 +00002320 class C3(tzinfo):
2321 def utcoffset(self, dt): return timedelta(minutes=-1439)
2322 def dst(self, dt): return timedelta(minutes=1439)
2323 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002324 t = cls(1, 1, 1, tzinfo=C3())
2325 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2326 self.assertEqual(t.dst(), timedelta(minutes=1439))
2327 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002328
2329 # Wrong types.
2330 class C4(tzinfo):
2331 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002332 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002333 def tzname(self, dt): return 0
2334 t = cls(1, 1, 1, tzinfo=C4())
2335 self.assertRaises(TypeError, t.utcoffset)
2336 self.assertRaises(TypeError, t.dst)
2337 self.assertRaises(TypeError, t.tzname)
2338
2339 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002340 class C6(tzinfo):
2341 def utcoffset(self, dt): return timedelta(hours=-24)
2342 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002343 t = cls(1, 1, 1, tzinfo=C6())
2344 self.assertRaises(ValueError, t.utcoffset)
2345 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002346
2347 # Not a whole number of minutes.
2348 class C7(tzinfo):
2349 def utcoffset(self, dt): return timedelta(seconds=61)
2350 def dst(self, dt): return timedelta(microseconds=-81)
2351 t = cls(1, 1, 1, tzinfo=C7())
2352 self.assertRaises(ValueError, t.utcoffset)
2353 self.assertRaises(ValueError, t.dst)
2354
Tim Peters4c0db782002-12-26 05:01:19 +00002355 def test_aware_compare(self):
2356 cls = self.theclass
2357
Tim Peters60c76e42002-12-27 00:41:11 +00002358 # Ensure that utcoffset() gets ignored if the comparands have
2359 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002360 class OperandDependentOffset(tzinfo):
2361 def utcoffset(self, t):
2362 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002363 # d0 and d1 equal after adjustment
2364 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002365 else:
Tim Peters397301e2003-01-02 21:28:08 +00002366 # d2 off in the weeds
2367 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002368
2369 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2370 d0 = base.replace(minute=3)
2371 d1 = base.replace(minute=9)
2372 d2 = base.replace(minute=11)
2373 for x in d0, d1, d2:
2374 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002375 for op in lt, le, gt, ge, eq, ne:
2376 got = op(x, y)
2377 expected = op(x.minute, y.minute)
2378 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002379
2380 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002381 # Note that a time can't actually have an operand-depedent offset,
2382 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2383 # so skip this test for time.
2384 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002385 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2386 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2387 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2388 for x in d0, d1, d2:
2389 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002390 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002391 if (x is d0 or x is d1) and (y is d0 or y is d1):
2392 expected = 0
2393 elif x is y is d2:
2394 expected = 0
2395 elif x is d2:
2396 expected = -1
2397 else:
2398 assert y is d2
2399 expected = 1
2400 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002401
Tim Peters855fe882002-12-22 03:43:39 +00002402
Tim Peters0bf60bd2003-01-08 20:40:01 +00002403# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002404class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002405 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002406
2407 def test_empty(self):
2408 t = self.theclass()
2409 self.assertEqual(t.hour, 0)
2410 self.assertEqual(t.minute, 0)
2411 self.assertEqual(t.second, 0)
2412 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002413 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002414
Tim Peters2a799bf2002-12-16 20:18:38 +00002415 def test_zones(self):
2416 est = FixedOffset(-300, "EST", 1)
2417 utc = FixedOffset(0, "UTC", -2)
2418 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002419 t1 = time( 7, 47, tzinfo=est)
2420 t2 = time(12, 47, tzinfo=utc)
2421 t3 = time(13, 47, tzinfo=met)
2422 t4 = time(microsecond=40)
2423 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002424
2425 self.assertEqual(t1.tzinfo, est)
2426 self.assertEqual(t2.tzinfo, utc)
2427 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002428 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002429 self.assertEqual(t5.tzinfo, utc)
2430
Tim Peters855fe882002-12-22 03:43:39 +00002431 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2432 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2433 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002434 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002435 self.assertRaises(TypeError, t1.utcoffset, "no args")
2436
2437 self.assertEqual(t1.tzname(), "EST")
2438 self.assertEqual(t2.tzname(), "UTC")
2439 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002440 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002441 self.assertRaises(TypeError, t1.tzname, "no args")
2442
Tim Peters855fe882002-12-22 03:43:39 +00002443 self.assertEqual(t1.dst(), timedelta(minutes=1))
2444 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2445 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002446 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002447 self.assertRaises(TypeError, t1.dst, "no args")
2448
2449 self.assertEqual(hash(t1), hash(t2))
2450 self.assertEqual(hash(t1), hash(t3))
2451 self.assertEqual(hash(t2), hash(t3))
2452
2453 self.assertEqual(t1, t2)
2454 self.assertEqual(t1, t3)
2455 self.assertEqual(t2, t3)
2456 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2457 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2458 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2459
2460 self.assertEqual(str(t1), "07:47:00-05:00")
2461 self.assertEqual(str(t2), "12:47:00+00:00")
2462 self.assertEqual(str(t3), "13:47:00+01:00")
2463 self.assertEqual(str(t4), "00:00:00.000040")
2464 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2465
2466 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2467 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2468 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2469 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2470 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2471
Tim Peters0bf60bd2003-01-08 20:40:01 +00002472 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002473 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2474 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2475 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2476 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2477 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2478
2479 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2480 "07:47:00 %Z=EST %z=-0500")
2481 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2482 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2483
2484 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002485 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002486 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2487 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2488
Tim Petersb92bb712002-12-21 17:44:07 +00002489 # Check that an invalid tzname result raises an exception.
2490 class Badtzname(tzinfo):
2491 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002492 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002493 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2494 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002495
2496 def test_hash_edge_cases(self):
2497 # Offsets that overflow a basic time.
2498 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2499 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2500 self.assertEqual(hash(t1), hash(t2))
2501
2502 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2503 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2504 self.assertEqual(hash(t1), hash(t2))
2505
Tim Peters2a799bf2002-12-16 20:18:38 +00002506 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002507 # Try one without a tzinfo.
2508 args = 20, 59, 16, 64**2
2509 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002510 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002511 green = pickler.dumps(orig, proto)
2512 derived = unpickler.loads(green)
2513 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002514
2515 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002516 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002517 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002518 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002519 green = pickler.dumps(orig, proto)
2520 derived = unpickler.loads(green)
2521 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002522 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002523 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2524 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002525
2526 def test_more_bool(self):
2527 # Test cases with non-None tzinfo.
2528 cls = self.theclass
2529
2530 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002531 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002532
2533 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002534 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002535
2536 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002537 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002538
2539 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002540 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002541
2542 # Mostly ensuring this doesn't overflow internally.
2543 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002544 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002545
2546 # But this should yield a value error -- the utcoffset is bogus.
2547 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2548 self.assertRaises(ValueError, lambda: bool(t))
2549
2550 # Likewise.
2551 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2552 self.assertRaises(ValueError, lambda: bool(t))
2553
Tim Peters12bf3392002-12-24 05:41:27 +00002554 def test_replace(self):
2555 cls = self.theclass
2556 z100 = FixedOffset(100, "+100")
2557 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2558 args = [1, 2, 3, 4, z100]
2559 base = cls(*args)
2560 self.assertEqual(base, base.replace())
2561
2562 i = 0
2563 for name, newval in (("hour", 5),
2564 ("minute", 6),
2565 ("second", 7),
2566 ("microsecond", 8),
2567 ("tzinfo", zm200)):
2568 newargs = args[:]
2569 newargs[i] = newval
2570 expected = cls(*newargs)
2571 got = base.replace(**{name: newval})
2572 self.assertEqual(expected, got)
2573 i += 1
2574
2575 # Ensure we can get rid of a tzinfo.
2576 self.assertEqual(base.tzname(), "+100")
2577 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002578 self.assertTrue(base2.tzinfo is None)
2579 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002580
2581 # Ensure we can add one.
2582 base3 = base2.replace(tzinfo=z100)
2583 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002584 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002585
2586 # Out of bounds.
2587 base = cls(1)
2588 self.assertRaises(ValueError, base.replace, hour=24)
2589 self.assertRaises(ValueError, base.replace, minute=-1)
2590 self.assertRaises(ValueError, base.replace, second=100)
2591 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2592
Tim Peters60c76e42002-12-27 00:41:11 +00002593 def test_mixed_compare(self):
2594 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002595 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002596 self.assertEqual(t1, t2)
2597 t2 = t2.replace(tzinfo=None)
2598 self.assertEqual(t1, t2)
2599 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2600 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002601 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2602 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002603
Tim Peters0bf60bd2003-01-08 20:40:01 +00002604 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002605 class Varies(tzinfo):
2606 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002607 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002608 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002609 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002610 return self.offset
2611
2612 v = Varies()
2613 t1 = t2.replace(tzinfo=v)
2614 t2 = t2.replace(tzinfo=v)
2615 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2616 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2617 self.assertEqual(t1, t2)
2618
2619 # But if they're not identical, it isn't ignored.
2620 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002621 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002622
Tim Petersa98924a2003-05-17 05:55:19 +00002623 def test_subclass_timetz(self):
2624
2625 class C(self.theclass):
2626 theAnswer = 42
2627
2628 def __new__(cls, *args, **kws):
2629 temp = kws.copy()
2630 extra = temp.pop('extra')
2631 result = self.theclass.__new__(cls, *args, **temp)
2632 result.extra = extra
2633 return result
2634
2635 def newmeth(self, start):
2636 return start + self.hour + self.second
2637
2638 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2639
2640 dt1 = self.theclass(*args)
2641 dt2 = C(*args, **{'extra': 7})
2642
2643 self.assertEqual(dt2.__class__, C)
2644 self.assertEqual(dt2.theAnswer, 42)
2645 self.assertEqual(dt2.extra, 7)
2646 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2647 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2648
Tim Peters4c0db782002-12-26 05:01:19 +00002649
Tim Peters0bf60bd2003-01-08 20:40:01 +00002650# Testing datetime objects with a non-None tzinfo.
2651
Guido van Rossumd8faa362007-04-27 19:54:29 +00002652class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002653 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002654
2655 def test_trivial(self):
2656 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2657 self.assertEqual(dt.year, 1)
2658 self.assertEqual(dt.month, 2)
2659 self.assertEqual(dt.day, 3)
2660 self.assertEqual(dt.hour, 4)
2661 self.assertEqual(dt.minute, 5)
2662 self.assertEqual(dt.second, 6)
2663 self.assertEqual(dt.microsecond, 7)
2664 self.assertEqual(dt.tzinfo, None)
2665
2666 def test_even_more_compare(self):
2667 # The test_compare() and test_more_compare() inherited from TestDate
2668 # and TestDateTime covered non-tzinfo cases.
2669
2670 # Smallest possible after UTC adjustment.
2671 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2672 # Largest possible after UTC adjustment.
2673 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2674 tzinfo=FixedOffset(-1439, ""))
2675
2676 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002677 self.assertTrue(t1 < t2)
2678 self.assertTrue(t1 != t2)
2679 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002680
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002681 self.assertEqual(t1, t1)
2682 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002683
2684 # Equal afer adjustment.
2685 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2686 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2687 self.assertEqual(t1, t2)
2688
2689 # Change t1 not to subtract a minute, and t1 should be larger.
2690 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002691 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002692
2693 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2694 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002695 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002696
2697 # Back to the original t1, but make seconds resolve it.
2698 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2699 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002700 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002701
2702 # Likewise, but make microseconds resolve it.
2703 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2704 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002705 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002706
2707 # Make t2 naive and it should fail.
2708 t2 = self.theclass.min
2709 self.assertRaises(TypeError, lambda: t1 == t2)
2710 self.assertEqual(t2, t2)
2711
2712 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2713 class Naive(tzinfo):
2714 def utcoffset(self, dt): return None
2715 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2716 self.assertRaises(TypeError, lambda: t1 == t2)
2717 self.assertEqual(t2, t2)
2718
2719 # OTOH, it's OK to compare two of these mixing the two ways of being
2720 # naive.
2721 t1 = self.theclass(5, 6, 7)
2722 self.assertEqual(t1, t2)
2723
2724 # Try a bogus uctoffset.
2725 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002726 def utcoffset(self, dt):
2727 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002728 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2729 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002730 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002731
Tim Peters2a799bf2002-12-16 20:18:38 +00002732 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002733 # Try one without a tzinfo.
2734 args = 6, 7, 23, 20, 59, 1, 64**2
2735 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002736 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002737 green = pickler.dumps(orig, proto)
2738 derived = unpickler.loads(green)
2739 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002740
2741 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002742 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002743 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002744 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002745 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002746 green = pickler.dumps(orig, proto)
2747 derived = unpickler.loads(green)
2748 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002749 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002750 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2751 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002752
2753 def test_extreme_hashes(self):
2754 # If an attempt is made to hash these via subtracting the offset
2755 # then hashing a datetime object, OverflowError results. The
2756 # Python implementation used to blow up here.
2757 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2758 hash(t)
2759 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2760 tzinfo=FixedOffset(-1439, ""))
2761 hash(t)
2762
2763 # OTOH, an OOB offset should blow up.
2764 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2765 self.assertRaises(ValueError, hash, t)
2766
2767 def test_zones(self):
2768 est = FixedOffset(-300, "EST")
2769 utc = FixedOffset(0, "UTC")
2770 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002771 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2772 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2773 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002774 self.assertEqual(t1.tzinfo, est)
2775 self.assertEqual(t2.tzinfo, utc)
2776 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002777 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2778 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2779 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002780 self.assertEqual(t1.tzname(), "EST")
2781 self.assertEqual(t2.tzname(), "UTC")
2782 self.assertEqual(t3.tzname(), "MET")
2783 self.assertEqual(hash(t1), hash(t2))
2784 self.assertEqual(hash(t1), hash(t3))
2785 self.assertEqual(hash(t2), hash(t3))
2786 self.assertEqual(t1, t2)
2787 self.assertEqual(t1, t3)
2788 self.assertEqual(t2, t3)
2789 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2790 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2791 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002792 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002793 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2794 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2795 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2796
2797 def test_combine(self):
2798 met = FixedOffset(60, "MET")
2799 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002800 tz = time(18, 45, 3, 1234, tzinfo=met)
2801 dt = datetime.combine(d, tz)
2802 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002803 tzinfo=met))
2804
2805 def test_extract(self):
2806 met = FixedOffset(60, "MET")
2807 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2808 self.assertEqual(dt.date(), date(2002, 3, 4))
2809 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002810 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002811
2812 def test_tz_aware_arithmetic(self):
2813 import random
2814
2815 now = self.theclass.now()
2816 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002817 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002818 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002819 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002820 self.assertEqual(nowaware.timetz(), timeaware)
2821
2822 # Can't mix aware and non-aware.
2823 self.assertRaises(TypeError, lambda: now - nowaware)
2824 self.assertRaises(TypeError, lambda: nowaware - now)
2825
Tim Peters0bf60bd2003-01-08 20:40:01 +00002826 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002827 self.assertRaises(TypeError, lambda: now + nowaware)
2828 self.assertRaises(TypeError, lambda: nowaware + now)
2829 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2830
2831 # Subtracting should yield 0.
2832 self.assertEqual(now - now, timedelta(0))
2833 self.assertEqual(nowaware - nowaware, timedelta(0))
2834
2835 # Adding a delta should preserve tzinfo.
2836 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2837 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002838 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002839 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002840 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002841 self.assertEqual(nowawareplus, nowawareplus2)
2842
2843 # that - delta should be what we started with, and that - what we
2844 # started with should be delta.
2845 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002846 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002847 self.assertEqual(nowaware, diff)
2848 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2849 self.assertEqual(nowawareplus - nowaware, delta)
2850
2851 # Make up a random timezone.
2852 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002853 # Attach it to nowawareplus.
2854 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002855 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002856 # Make sure the difference takes the timezone adjustments into account.
2857 got = nowaware - nowawareplus
2858 # Expected: (nowaware base - nowaware offset) -
2859 # (nowawareplus base - nowawareplus offset) =
2860 # (nowaware base - nowawareplus base) +
2861 # (nowawareplus offset - nowaware offset) =
2862 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002863 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002864 self.assertEqual(got, expected)
2865
2866 # Try max possible difference.
2867 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2868 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2869 tzinfo=FixedOffset(-1439, "max"))
2870 maxdiff = max - min
2871 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2872 timedelta(minutes=2*1439))
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00002873 # Different tzinfo, but the same offset
2874 tza = timezone(HOUR, 'A')
2875 tzb = timezone(HOUR, 'B')
2876 delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
2877 self.assertEqual(delta, self.theclass.min - self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00002878
2879 def test_tzinfo_now(self):
2880 meth = self.theclass.now
2881 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2882 base = meth()
2883 # Try with and without naming the keyword.
2884 off42 = FixedOffset(42, "42")
2885 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002886 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002887 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002888 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002889 # Bad argument with and w/o naming the keyword.
2890 self.assertRaises(TypeError, meth, 16)
2891 self.assertRaises(TypeError, meth, tzinfo=16)
2892 # Bad keyword name.
2893 self.assertRaises(TypeError, meth, tinfo=off42)
2894 # Too many args.
2895 self.assertRaises(TypeError, meth, off42, off42)
2896
Tim Peters10cadce2003-01-23 19:58:02 +00002897 # We don't know which time zone we're in, and don't have a tzinfo
2898 # class to represent it, so seeing whether a tz argument actually
2899 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002900 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002901 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2902 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2903 for dummy in range(3):
2904 now = datetime.now(weirdtz)
2905 self.assertTrue(now.tzinfo is weirdtz)
2906 utcnow = datetime.utcnow().replace(tzinfo=utc)
2907 now2 = utcnow.astimezone(weirdtz)
2908 if abs(now - now2) < timedelta(seconds=30):
2909 break
2910 # Else the code is broken, or more than 30 seconds passed between
2911 # calls; assuming the latter, just try again.
2912 else:
2913 # Three strikes and we're out.
2914 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002915
Tim Peters2a799bf2002-12-16 20:18:38 +00002916 def test_tzinfo_fromtimestamp(self):
2917 import time
2918 meth = self.theclass.fromtimestamp
2919 ts = time.time()
2920 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2921 base = meth(ts)
2922 # Try with and without naming the keyword.
2923 off42 = FixedOffset(42, "42")
2924 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002925 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002926 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002927 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002928 # Bad argument with and w/o naming the keyword.
2929 self.assertRaises(TypeError, meth, ts, 16)
2930 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2931 # Bad keyword name.
2932 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2933 # Too many args.
2934 self.assertRaises(TypeError, meth, ts, off42, off42)
2935 # Too few args.
2936 self.assertRaises(TypeError, meth)
2937
Tim Peters2a44a8d2003-01-23 20:53:10 +00002938 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002939 timestamp = 1000000000
2940 utcdatetime = datetime.utcfromtimestamp(timestamp)
2941 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2942 # But on some flavor of Mac, it's nowhere near that. So we can't have
2943 # any idea here what time that actually is, we can only test that
2944 # relative changes match.
2945 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2946 tz = FixedOffset(utcoffset, "tz", 0)
2947 expected = utcdatetime + utcoffset
2948 got = datetime.fromtimestamp(timestamp, tz)
2949 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002950
Tim Peters2a799bf2002-12-16 20:18:38 +00002951 def test_tzinfo_utcnow(self):
2952 meth = self.theclass.utcnow
2953 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2954 base = meth()
2955 # Try with and without naming the keyword; for whatever reason,
2956 # utcnow() doesn't accept a tzinfo argument.
2957 off42 = FixedOffset(42, "42")
2958 self.assertRaises(TypeError, meth, off42)
2959 self.assertRaises(TypeError, meth, tzinfo=off42)
2960
2961 def test_tzinfo_utcfromtimestamp(self):
2962 import time
2963 meth = self.theclass.utcfromtimestamp
2964 ts = time.time()
2965 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2966 base = meth(ts)
2967 # Try with and without naming the keyword; for whatever reason,
2968 # utcfromtimestamp() doesn't accept a tzinfo argument.
2969 off42 = FixedOffset(42, "42")
2970 self.assertRaises(TypeError, meth, ts, off42)
2971 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2972
2973 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002974 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002975 # DST flag.
2976 class DST(tzinfo):
2977 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002978 if isinstance(dstvalue, int):
2979 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002980 self.dstvalue = dstvalue
2981 def dst(self, dt):
2982 return self.dstvalue
2983
2984 cls = self.theclass
2985 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2986 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2987 t = d.timetuple()
2988 self.assertEqual(1, t.tm_year)
2989 self.assertEqual(1, t.tm_mon)
2990 self.assertEqual(1, t.tm_mday)
2991 self.assertEqual(10, t.tm_hour)
2992 self.assertEqual(20, t.tm_min)
2993 self.assertEqual(30, t.tm_sec)
2994 self.assertEqual(0, t.tm_wday)
2995 self.assertEqual(1, t.tm_yday)
2996 self.assertEqual(flag, t.tm_isdst)
2997
2998 # dst() returns wrong type.
2999 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
3000
3001 # dst() at the edge.
3002 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
3003 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
3004
3005 # dst() out of range.
3006 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
3007 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
3008
3009 def test_utctimetuple(self):
3010 class DST(tzinfo):
Alexander Belopolskyf34e82e2010-07-05 15:05:33 +00003011 def __init__(self, dstvalue=0):
Tim Peters397301e2003-01-02 21:28:08 +00003012 if isinstance(dstvalue, int):
3013 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00003014 self.dstvalue = dstvalue
3015 def dst(self, dt):
3016 return self.dstvalue
3017
3018 cls = self.theclass
3019 # This can't work: DST didn't implement utcoffset.
3020 self.assertRaises(NotImplementedError,
3021 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3022
3023 class UOFS(DST):
3024 def __init__(self, uofs, dofs=None):
3025 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00003026 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00003027 def utcoffset(self, dt):
3028 return self.uofs
3029
Tim Peters2a799bf2002-12-16 20:18:38 +00003030 for dstvalue in -33, 33, 0, None:
3031 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3032 t = d.utctimetuple()
3033 self.assertEqual(d.year, t.tm_year)
3034 self.assertEqual(d.month, t.tm_mon)
3035 self.assertEqual(d.day, t.tm_mday)
3036 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3037 self.assertEqual(13, t.tm_min)
3038 self.assertEqual(d.second, t.tm_sec)
3039 self.assertEqual(d.weekday(), t.tm_wday)
3040 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3041 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003042 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3043 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003044 self.assertEqual(0, t.tm_isdst)
3045
Alexander Belopolskyf34e82e2010-07-05 15:05:33 +00003046 # For naive datetime, utctimetuple == timetuple except for isdst
3047 d = cls(1, 2, 3, 10, 20, 30, 40)
3048 t = d.utctimetuple()
3049 self.assertEqual(t[:-1], d.timetuple()[:-1])
3050 self.assertEqual(0, t.tm_isdst)
3051 # Same if utcoffset is None
3052 class NOFS(DST):
3053 def utcoffset(self, dt):
3054 return None
3055 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
3056 t = d.utctimetuple()
3057 self.assertEqual(t[:-1], d.timetuple()[:-1])
3058 self.assertEqual(0, t.tm_isdst)
3059 # Check that bad tzinfo is detected
3060 class BOFS(DST):
3061 def utcoffset(self, dt):
3062 return "EST"
3063 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
3064 self.assertRaises(TypeError, d.utctimetuple)
3065
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003066 # Check that utctimetuple() is the same as
3067 # astimezone(utc).timetuple()
3068 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3069 for tz in [timezone.min, timezone.utc, timezone.max]:
3070 dtz = d.replace(tzinfo=tz)
3071 self.assertEqual(dtz.utctimetuple()[:-1],
3072 dtz.astimezone(timezone.utc).timetuple()[:-1])
3073 # At the edges, UTC adjustment can produce years out-of-range
3074 # for a datetime object. Ensure that an OverflowError is
3075 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003076 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3077 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003078 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003079
3080 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3081 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003082 self.assertRaises(OverflowError, huge.utctimetuple)
3083 # More overflow cases
3084 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3085 self.assertRaises(OverflowError, tiny.utctimetuple)
3086 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3087 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003088
3089 def test_tzinfo_isoformat(self):
3090 zero = FixedOffset(0, "+00:00")
3091 plus = FixedOffset(220, "+03:40")
3092 minus = FixedOffset(-231, "-03:51")
3093 unknown = FixedOffset(None, "")
3094
3095 cls = self.theclass
3096 datestr = '0001-02-03'
3097 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003098 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003099 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3100 timestr = '04:05:59' + (us and '.987001' or '')
3101 ofsstr = ofs is not None and d.tzname() or ''
3102 tailstr = timestr + ofsstr
3103 iso = d.isoformat()
3104 self.assertEqual(iso, datestr + 'T' + tailstr)
3105 self.assertEqual(iso, d.isoformat('T'))
3106 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003107 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003108 self.assertEqual(str(d), datestr + ' ' + tailstr)
3109
Tim Peters12bf3392002-12-24 05:41:27 +00003110 def test_replace(self):
3111 cls = self.theclass
3112 z100 = FixedOffset(100, "+100")
3113 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3114 args = [1, 2, 3, 4, 5, 6, 7, z100]
3115 base = cls(*args)
3116 self.assertEqual(base, base.replace())
3117
3118 i = 0
3119 for name, newval in (("year", 2),
3120 ("month", 3),
3121 ("day", 4),
3122 ("hour", 5),
3123 ("minute", 6),
3124 ("second", 7),
3125 ("microsecond", 8),
3126 ("tzinfo", zm200)):
3127 newargs = args[:]
3128 newargs[i] = newval
3129 expected = cls(*newargs)
3130 got = base.replace(**{name: newval})
3131 self.assertEqual(expected, got)
3132 i += 1
3133
3134 # Ensure we can get rid of a tzinfo.
3135 self.assertEqual(base.tzname(), "+100")
3136 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003137 self.assertTrue(base2.tzinfo is None)
3138 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003139
3140 # Ensure we can add one.
3141 base3 = base2.replace(tzinfo=z100)
3142 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003143 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003144
3145 # Out of bounds.
3146 base = cls(2000, 2, 29)
3147 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003148
Tim Peters80475bb2002-12-25 07:40:55 +00003149 def test_more_astimezone(self):
3150 # The inherited test_astimezone covered some trivial and error cases.
3151 fnone = FixedOffset(None, "None")
3152 f44m = FixedOffset(44, "44")
3153 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3154
Tim Peters10cadce2003-01-23 19:58:02 +00003155 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003156 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003157 # Replacing with degenerate tzinfo raises an exception.
3158 self.assertRaises(ValueError, dt.astimezone, fnone)
3159 # Ditto with None tz.
3160 self.assertRaises(TypeError, dt.astimezone, None)
3161 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003162 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003163 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003164 self.assertEqual(x.date(), dt.date())
3165 self.assertEqual(x.time(), dt.time())
3166
3167 # Replacing with different tzinfo does adjust.
3168 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003169 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003170 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3171 expected = dt - dt.utcoffset() # in effect, convert to UTC
3172 expected += fm5h.utcoffset(dt) # and from there to local time
3173 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3174 self.assertEqual(got.date(), expected.date())
3175 self.assertEqual(got.time(), expected.time())
3176 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003177 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003178 self.assertEqual(got, expected)
3179
Tim Peters4c0db782002-12-26 05:01:19 +00003180 def test_aware_subtract(self):
3181 cls = self.theclass
3182
Tim Peters60c76e42002-12-27 00:41:11 +00003183 # Ensure that utcoffset() is ignored when the operands have the
3184 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003185 class OperandDependentOffset(tzinfo):
3186 def utcoffset(self, t):
3187 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003188 # d0 and d1 equal after adjustment
3189 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003190 else:
Tim Peters397301e2003-01-02 21:28:08 +00003191 # d2 off in the weeds
3192 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003193
3194 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3195 d0 = base.replace(minute=3)
3196 d1 = base.replace(minute=9)
3197 d2 = base.replace(minute=11)
3198 for x in d0, d1, d2:
3199 for y in d0, d1, d2:
3200 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003201 expected = timedelta(minutes=x.minute - y.minute)
3202 self.assertEqual(got, expected)
3203
3204 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3205 # ignored.
3206 base = cls(8, 9, 10, 11, 12, 13, 14)
3207 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3208 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3209 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3210 for x in d0, d1, d2:
3211 for y in d0, d1, d2:
3212 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003213 if (x is d0 or x is d1) and (y is d0 or y is d1):
3214 expected = timedelta(0)
3215 elif x is y is d2:
3216 expected = timedelta(0)
3217 elif x is d2:
3218 expected = timedelta(minutes=(11-59)-0)
3219 else:
3220 assert y is d2
3221 expected = timedelta(minutes=0-(11-59))
3222 self.assertEqual(got, expected)
3223
Tim Peters60c76e42002-12-27 00:41:11 +00003224 def test_mixed_compare(self):
3225 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003226 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003227 self.assertEqual(t1, t2)
3228 t2 = t2.replace(tzinfo=None)
3229 self.assertEqual(t1, t2)
3230 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3231 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003232 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3233 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003234
Tim Peters0bf60bd2003-01-08 20:40:01 +00003235 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003236 class Varies(tzinfo):
3237 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003238 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003239 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003240 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003241 return self.offset
3242
3243 v = Varies()
3244 t1 = t2.replace(tzinfo=v)
3245 t2 = t2.replace(tzinfo=v)
3246 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3247 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3248 self.assertEqual(t1, t2)
3249
3250 # But if they're not identical, it isn't ignored.
3251 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003252 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003253
Tim Petersa98924a2003-05-17 05:55:19 +00003254 def test_subclass_datetimetz(self):
3255
3256 class C(self.theclass):
3257 theAnswer = 42
3258
3259 def __new__(cls, *args, **kws):
3260 temp = kws.copy()
3261 extra = temp.pop('extra')
3262 result = self.theclass.__new__(cls, *args, **temp)
3263 result.extra = extra
3264 return result
3265
3266 def newmeth(self, start):
3267 return start + self.hour + self.year
3268
3269 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3270
3271 dt1 = self.theclass(*args)
3272 dt2 = C(*args, **{'extra': 7})
3273
3274 self.assertEqual(dt2.__class__, C)
3275 self.assertEqual(dt2.theAnswer, 42)
3276 self.assertEqual(dt2.extra, 7)
3277 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3278 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3279
Tim Peters621818b2002-12-29 23:44:49 +00003280# Pain to set up DST-aware tzinfo classes.
3281
3282def first_sunday_on_or_after(dt):
3283 days_to_go = 6 - dt.weekday()
3284 if days_to_go:
3285 dt += timedelta(days_to_go)
3286 return dt
3287
3288ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003289MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003290HOUR = timedelta(hours=1)
3291DAY = timedelta(days=1)
3292# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3293DSTSTART = datetime(1, 4, 1, 2)
3294# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003295# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3296# being standard time on that day, there is no spelling in local time of
3297# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3298DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003299
3300class USTimeZone(tzinfo):
3301
3302 def __init__(self, hours, reprname, stdname, dstname):
3303 self.stdoffset = timedelta(hours=hours)
3304 self.reprname = reprname
3305 self.stdname = stdname
3306 self.dstname = dstname
3307
3308 def __repr__(self):
3309 return self.reprname
3310
3311 def tzname(self, dt):
3312 if self.dst(dt):
3313 return self.dstname
3314 else:
3315 return self.stdname
3316
3317 def utcoffset(self, dt):
3318 return self.stdoffset + self.dst(dt)
3319
3320 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003321 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003322 # An exception instead may be sensible here, in one or more of
3323 # the cases.
3324 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003325 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003326
3327 # Find first Sunday in April.
3328 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3329 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3330
3331 # Find last Sunday in October.
3332 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3333 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3334
Tim Peters621818b2002-12-29 23:44:49 +00003335 # Can't compare naive to aware objects, so strip the timezone from
3336 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003337 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003338 return HOUR
3339 else:
3340 return ZERO
3341
Tim Peters521fc152002-12-31 17:36:56 +00003342Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3343Central = USTimeZone(-6, "Central", "CST", "CDT")
3344Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3345Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003346utc_real = FixedOffset(0, "UTC", 0)
3347# For better test coverage, we want another flavor of UTC that's west of
3348# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003349utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003350
3351class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003352 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003353 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003354 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003355
Tim Peters0bf60bd2003-01-08 20:40:01 +00003356 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003357
Tim Peters521fc152002-12-31 17:36:56 +00003358 # Check a time that's inside DST.
3359 def checkinside(self, dt, tz, utc, dston, dstoff):
3360 self.assertEqual(dt.dst(), HOUR)
3361
3362 # Conversion to our own timezone is always an identity.
3363 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003364
3365 asutc = dt.astimezone(utc)
3366 there_and_back = asutc.astimezone(tz)
3367
3368 # Conversion to UTC and back isn't always an identity here,
3369 # because there are redundant spellings (in local time) of
3370 # UTC time when DST begins: the clock jumps from 1:59:59
3371 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3372 # make sense then. The classes above treat 2:MM:SS as
3373 # daylight time then (it's "after 2am"), really an alias
3374 # for 1:MM:SS standard time. The latter form is what
3375 # conversion back from UTC produces.
3376 if dt.date() == dston.date() and dt.hour == 2:
3377 # We're in the redundant hour, and coming back from
3378 # UTC gives the 1:MM:SS standard-time spelling.
3379 self.assertEqual(there_and_back + HOUR, dt)
3380 # Although during was considered to be in daylight
3381 # time, there_and_back is not.
3382 self.assertEqual(there_and_back.dst(), ZERO)
3383 # They're the same times in UTC.
3384 self.assertEqual(there_and_back.astimezone(utc),
3385 dt.astimezone(utc))
3386 else:
3387 # We're not in the redundant hour.
3388 self.assertEqual(dt, there_and_back)
3389
Tim Peters327098a2003-01-20 22:54:38 +00003390 # Because we have a redundant spelling when DST begins, there is
3391 # (unforunately) an hour when DST ends that can't be spelled at all in
3392 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3393 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3394 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3395 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3396 # expressed in local time. Nevertheless, we want conversion back
3397 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003398 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003399 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003400 if dt.date() == dstoff.date() and dt.hour == 0:
3401 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003402 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003403 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3404 nexthour_utc += HOUR
3405 nexthour_tz = nexthour_utc.astimezone(tz)
3406 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003407 else:
Tim Peters327098a2003-01-20 22:54:38 +00003408 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003409
3410 # Check a time that's outside DST.
3411 def checkoutside(self, dt, tz, utc):
3412 self.assertEqual(dt.dst(), ZERO)
3413
3414 # Conversion to our own timezone is always an identity.
3415 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003416
3417 # Converting to UTC and back is an identity too.
3418 asutc = dt.astimezone(utc)
3419 there_and_back = asutc.astimezone(tz)
3420 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003421
Tim Peters1024bf82002-12-30 17:09:40 +00003422 def convert_between_tz_and_utc(self, tz, utc):
3423 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003424 # Because 1:MM on the day DST ends is taken as being standard time,
3425 # there is no spelling in tz for the last hour of daylight time.
3426 # For purposes of the test, the last hour of DST is 0:MM, which is
3427 # taken as being daylight time (and 1:MM is taken as being standard
3428 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003429 dstoff = self.dstoff.replace(tzinfo=tz)
3430 for delta in (timedelta(weeks=13),
3431 DAY,
3432 HOUR,
3433 timedelta(minutes=1),
3434 timedelta(microseconds=1)):
3435
Tim Peters521fc152002-12-31 17:36:56 +00003436 self.checkinside(dston, tz, utc, dston, dstoff)
3437 for during in dston + delta, dstoff - delta:
3438 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003439
Tim Peters521fc152002-12-31 17:36:56 +00003440 self.checkoutside(dstoff, tz, utc)
3441 for outside in dston - delta, dstoff + delta:
3442 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003443
Tim Peters621818b2002-12-29 23:44:49 +00003444 def test_easy(self):
3445 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003446 self.convert_between_tz_and_utc(Eastern, utc_real)
3447 self.convert_between_tz_and_utc(Pacific, utc_real)
3448 self.convert_between_tz_and_utc(Eastern, utc_fake)
3449 self.convert_between_tz_and_utc(Pacific, utc_fake)
3450 # The next is really dancing near the edge. It works because
3451 # Pacific and Eastern are far enough apart that their "problem
3452 # hours" don't overlap.
3453 self.convert_between_tz_and_utc(Eastern, Pacific)
3454 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003455 # OTOH, these fail! Don't enable them. The difficulty is that
3456 # the edge case tests assume that every hour is representable in
3457 # the "utc" class. This is always true for a fixed-offset tzinfo
3458 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3459 # For these adjacent DST-aware time zones, the range of time offsets
3460 # tested ends up creating hours in the one that aren't representable
3461 # in the other. For the same reason, we would see failures in the
3462 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3463 # offset deltas in convert_between_tz_and_utc().
3464 #
3465 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3466 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003467
Tim Petersf3615152003-01-01 21:51:37 +00003468 def test_tricky(self):
3469 # 22:00 on day before daylight starts.
3470 fourback = self.dston - timedelta(hours=4)
3471 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003472 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003473 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3474 # 2", we should get the 3 spelling.
3475 # If we plug 22:00 the day before into Eastern, it "looks like std
3476 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3477 # to 22:00 lands on 2:00, which makes no sense in local time (the
3478 # local clock jumps from 1 to 3). The point here is to make sure we
3479 # get the 3 spelling.
3480 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003481 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003482 self.assertEqual(expected, got)
3483
3484 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3485 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003486 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003487 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3488 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3489 # spelling.
3490 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003491 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003492 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003493
Tim Petersadf64202003-01-04 06:03:15 +00003494 # Now on the day DST ends, we want "repeat an hour" behavior.
3495 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3496 # EST 23:MM 0:MM 1:MM 2:MM
3497 # EDT 0:MM 1:MM 2:MM 3:MM
3498 # wall 0:MM 1:MM 1:MM 2:MM against these
3499 for utc in utc_real, utc_fake:
3500 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003501 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003502 # Convert that to UTC.
3503 first_std_hour -= tz.utcoffset(None)
3504 # Adjust for possibly fake UTC.
3505 asutc = first_std_hour + utc.utcoffset(None)
3506 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3507 # tz=Eastern.
3508 asutcbase = asutc.replace(tzinfo=utc)
3509 for tzhour in (0, 1, 1, 2):
3510 expectedbase = self.dstoff.replace(hour=tzhour)
3511 for minute in 0, 30, 59:
3512 expected = expectedbase.replace(minute=minute)
3513 asutc = asutcbase.replace(minute=minute)
3514 astz = asutc.astimezone(tz)
3515 self.assertEqual(astz.replace(tzinfo=None), expected)
3516 asutcbase += HOUR
3517
3518
Tim Peters710fb152003-01-02 19:35:54 +00003519 def test_bogus_dst(self):
3520 class ok(tzinfo):
3521 def utcoffset(self, dt): return HOUR
3522 def dst(self, dt): return HOUR
3523
3524 now = self.theclass.now().replace(tzinfo=utc_real)
3525 # Doesn't blow up.
3526 now.astimezone(ok())
3527
3528 # Does blow up.
3529 class notok(ok):
3530 def dst(self, dt): return None
3531 self.assertRaises(ValueError, now.astimezone, notok())
3532
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003533 # Sometimes blow up. In the following, tzinfo.dst()
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00003534 # implementation may return None or not None depending on
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003535 # whether DST is assumed to be in effect. In this situation,
3536 # a ValueError should be raised by astimezone().
3537 class tricky_notok(ok):
3538 def dst(self, dt):
3539 if dt.year == 2000:
3540 return None
3541 else:
3542 return 10*HOUR
3543 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3544 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3545
Tim Peters52dcce22003-01-23 16:36:11 +00003546 def test_fromutc(self):
3547 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3548 now = datetime.utcnow().replace(tzinfo=utc_real)
3549 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3550 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3551 enow = Eastern.fromutc(now) # doesn't blow up
3552 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3553 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3554 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3555
3556 # Always converts UTC to standard time.
3557 class FauxUSTimeZone(USTimeZone):
3558 def fromutc(self, dt):
3559 return dt + self.stdoffset
3560 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3561
3562 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3563 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3564 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3565
3566 # Check around DST start.
3567 start = self.dston.replace(hour=4, tzinfo=Eastern)
3568 fstart = start.replace(tzinfo=FEastern)
3569 for wall in 23, 0, 1, 3, 4, 5:
3570 expected = start.replace(hour=wall)
3571 if wall == 23:
3572 expected -= timedelta(days=1)
3573 got = Eastern.fromutc(start)
3574 self.assertEqual(expected, got)
3575
3576 expected = fstart + FEastern.stdoffset
3577 got = FEastern.fromutc(fstart)
3578 self.assertEqual(expected, got)
3579
3580 # Ensure astimezone() calls fromutc() too.
3581 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3582 self.assertEqual(expected, got)
3583
3584 start += HOUR
3585 fstart += HOUR
3586
3587 # Check around DST end.
3588 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3589 fstart = start.replace(tzinfo=FEastern)
3590 for wall in 0, 1, 1, 2, 3, 4:
3591 expected = start.replace(hour=wall)
3592 got = Eastern.fromutc(start)
3593 self.assertEqual(expected, got)
3594
3595 expected = fstart + FEastern.stdoffset
3596 got = FEastern.fromutc(fstart)
3597 self.assertEqual(expected, got)
3598
3599 # Ensure astimezone() calls fromutc() too.
3600 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3601 self.assertEqual(expected, got)
3602
3603 start += HOUR
3604 fstart += HOUR
3605
Tim Peters710fb152003-01-02 19:35:54 +00003606
Tim Peters528ca532004-09-16 01:30:50 +00003607#############################################################################
3608# oddballs
3609
3610class Oddballs(unittest.TestCase):
3611
3612 def test_bug_1028306(self):
3613 # Trying to compare a date to a datetime should act like a mixed-
3614 # type comparison, despite that datetime is a subclass of date.
3615 as_date = date.today()
3616 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003617 self.assertTrue(as_date != as_datetime)
3618 self.assertTrue(as_datetime != as_date)
3619 self.assertTrue(not as_date == as_datetime)
3620 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003621 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3622 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3623 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3624 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3625 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3626 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3627 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3628 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3629
3630 # Neverthelss, comparison should work with the base-class (date)
3631 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003632 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003633 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003634 as_different = as_datetime.replace(day= different_day)
3635 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003636
3637 # And date should compare with other subclasses of date. If a
3638 # subclass wants to stop this, it's up to the subclass to do so.
3639 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3640 self.assertEqual(as_date, date_sc)
3641 self.assertEqual(date_sc, as_date)
3642
3643 # Ditto for datetimes.
3644 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3645 as_date.day, 0, 0, 0)
3646 self.assertEqual(as_datetime, datetime_sc)
3647 self.assertEqual(datetime_sc, as_datetime)
3648
Tim Peters2a799bf2002-12-16 20:18:38 +00003649def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003650 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003651
3652if __name__ == "__main__":
3653 test_main()