blob: c12bfb2241a629e96162b0c59de1eab7b0ff3934 [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)
Alexander Belopolskyd87e9322010-07-05 17:57:31 +0000884 c = self.theclass(2001,2,1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000885
886 diff = a-b
887 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
888 self.assertEqual(diff.seconds, 0)
889 self.assertEqual(diff.microseconds, 0)
890
891 day = timedelta(1)
892 week = timedelta(7)
893 a = self.theclass(2002, 3, 2)
894 self.assertEqual(a + day, self.theclass(2002, 3, 3))
895 self.assertEqual(day + a, self.theclass(2002, 3, 3))
896 self.assertEqual(a - day, self.theclass(2002, 3, 1))
897 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
898 self.assertEqual(a + week, self.theclass(2002, 3, 9))
899 self.assertEqual(a - week, self.theclass(2002, 2, 23))
900 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
901 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
902 self.assertEqual((a + week) - a, week)
903 self.assertEqual((a + day) - a, day)
904 self.assertEqual((a - week) - a, -week)
905 self.assertEqual((a - day) - a, -day)
906 self.assertEqual(a - (a + week), -week)
907 self.assertEqual(a - (a + day), -day)
908 self.assertEqual(a - (a - week), week)
909 self.assertEqual(a - (a - day), day)
Alexander Belopolskyd87e9322010-07-05 17:57:31 +0000910 self.assertEqual(c - (c - day), day)
Tim Peters2a799bf2002-12-16 20:18:38 +0000911
Mark Dickinson5c2db372009-12-05 20:28:34 +0000912 # Add/sub ints or floats should be illegal
913 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000914 self.assertRaises(TypeError, lambda: a+i)
915 self.assertRaises(TypeError, lambda: a-i)
916 self.assertRaises(TypeError, lambda: i+a)
917 self.assertRaises(TypeError, lambda: i-a)
918
919 # delta - date is senseless.
920 self.assertRaises(TypeError, lambda: day - a)
921 # mixing date and (delta or date) via * or // is senseless
922 self.assertRaises(TypeError, lambda: day * a)
923 self.assertRaises(TypeError, lambda: a * day)
924 self.assertRaises(TypeError, lambda: day // a)
925 self.assertRaises(TypeError, lambda: a // day)
926 self.assertRaises(TypeError, lambda: a * a)
927 self.assertRaises(TypeError, lambda: a // a)
928 # date + date is senseless
929 self.assertRaises(TypeError, lambda: a + a)
930
931 def test_overflow(self):
932 tiny = self.theclass.resolution
933
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000934 for delta in [tiny, timedelta(1), timedelta(2)]:
935 dt = self.theclass.min + delta
936 dt -= delta # no problem
937 self.assertRaises(OverflowError, dt.__sub__, delta)
938 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000939
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000940 dt = self.theclass.max - delta
941 dt += delta # no problem
942 self.assertRaises(OverflowError, dt.__add__, delta)
943 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000944
945 def test_fromtimestamp(self):
946 import time
947
948 # Try an arbitrary fixed value.
949 year, month, day = 1999, 9, 19
950 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
951 d = self.theclass.fromtimestamp(ts)
952 self.assertEqual(d.year, year)
953 self.assertEqual(d.month, month)
954 self.assertEqual(d.day, day)
955
Tim Peters1b6f7a92004-06-20 02:50:16 +0000956 def test_insane_fromtimestamp(self):
957 # It's possible that some platform maps time_t to double,
958 # and that this test will fail there. This test should
959 # exempt such platforms (provided they return reasonable
960 # results!).
961 for insane in -1e200, 1e200:
962 self.assertRaises(ValueError, self.theclass.fromtimestamp,
963 insane)
964
Tim Peters2a799bf2002-12-16 20:18:38 +0000965 def test_today(self):
966 import time
967
968 # We claim that today() is like fromtimestamp(time.time()), so
969 # prove it.
970 for dummy in range(3):
971 today = self.theclass.today()
972 ts = time.time()
973 todayagain = self.theclass.fromtimestamp(ts)
974 if today == todayagain:
975 break
976 # There are several legit reasons that could fail:
977 # 1. It recently became midnight, between the today() and the
978 # time() calls.
979 # 2. The platform time() has such fine resolution that we'll
980 # never get the same value twice.
981 # 3. The platform time() has poor resolution, and we just
982 # happened to call today() right before a resolution quantum
983 # boundary.
984 # 4. The system clock got fiddled between calls.
985 # In any case, wait a little while and try again.
986 time.sleep(0.1)
987
988 # It worked or it didn't. If it didn't, assume it's reason #2, and
989 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000990 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000991 abs(todayagain - today) < timedelta(seconds=0.5))
992
993 def test_weekday(self):
994 for i in range(7):
995 # March 4, 2002 is a Monday
996 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
997 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
998 # January 2, 1956 is a Monday
999 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
1000 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
1001
1002 def test_isocalendar(self):
1003 # Check examples from
1004 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1005 for i in range(7):
1006 d = self.theclass(2003, 12, 22+i)
1007 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1008 d = self.theclass(2003, 12, 29) + timedelta(i)
1009 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1010 d = self.theclass(2004, 1, 5+i)
1011 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1012 d = self.theclass(2009, 12, 21+i)
1013 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1014 d = self.theclass(2009, 12, 28) + timedelta(i)
1015 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1016 d = self.theclass(2010, 1, 4+i)
1017 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1018
1019 def test_iso_long_years(self):
1020 # Calculate long ISO years and compare to table from
1021 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1022 ISO_LONG_YEARS_TABLE = """
1023 4 32 60 88
1024 9 37 65 93
1025 15 43 71 99
1026 20 48 76
1027 26 54 82
1028
1029 105 133 161 189
1030 111 139 167 195
1031 116 144 172
1032 122 150 178
1033 128 156 184
1034
1035 201 229 257 285
1036 207 235 263 291
1037 212 240 268 296
1038 218 246 274
1039 224 252 280
1040
1041 303 331 359 387
1042 308 336 364 392
1043 314 342 370 398
1044 320 348 376
1045 325 353 381
1046 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001047 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001048 L = []
1049 for i in range(400):
1050 d = self.theclass(2000+i, 12, 31)
1051 d1 = self.theclass(1600+i, 12, 31)
1052 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1053 if d.isocalendar()[1] == 53:
1054 L.append(i)
1055 self.assertEqual(L, iso_long_years)
1056
1057 def test_isoformat(self):
1058 t = self.theclass(2, 3, 2)
1059 self.assertEqual(t.isoformat(), "0002-03-02")
1060
1061 def test_ctime(self):
1062 t = self.theclass(2002, 3, 2)
1063 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1064
1065 def test_strftime(self):
1066 t = self.theclass(2005, 3, 2)
1067 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001068 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001069 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001070
1071 self.assertRaises(TypeError, t.strftime) # needs an arg
1072 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1073 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1074
Georg Brandlf78e02b2008-06-10 17:40:04 +00001075 # test that unicode input is allowed (issue 2782)
1076 self.assertEqual(t.strftime("%m"), "03")
1077
Tim Peters2a799bf2002-12-16 20:18:38 +00001078 # A naive object replaces %z and %Z w/ empty strings.
1079 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1080
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001081 #make sure that invalid format specifiers are handled correctly
1082 #self.assertRaises(ValueError, t.strftime, "%e")
1083 #self.assertRaises(ValueError, t.strftime, "%")
1084 #self.assertRaises(ValueError, t.strftime, "%#")
1085
1086 #oh well, some systems just ignore those invalid ones.
1087 #at least, excercise them to make sure that no crashes
1088 #are generated
1089 for f in ["%e", "%", "%#"]:
1090 try:
1091 t.strftime(f)
1092 except ValueError:
1093 pass
1094
1095 #check that this standard extension works
1096 t.strftime("%f")
1097
Georg Brandlf78e02b2008-06-10 17:40:04 +00001098
Eric Smith1ba31142007-09-11 18:06:02 +00001099 def test_format(self):
1100 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001101 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001102
1103 # check that a derived class's __str__() gets called
1104 class A(self.theclass):
1105 def __str__(self):
1106 return 'A'
1107 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001108 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001109
1110 # check that a derived class's strftime gets called
1111 class B(self.theclass):
1112 def strftime(self, format_spec):
1113 return 'B'
1114 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001115 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001116
1117 for fmt in ["m:%m d:%d y:%y",
1118 "m:%m d:%d y:%y H:%H M:%M S:%S",
1119 "%z %Z",
1120 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001121 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1122 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1123 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001124
Tim Peters2a799bf2002-12-16 20:18:38 +00001125 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001126 # XXX: Should min and max respect subclassing?
1127 if issubclass(self.theclass, datetime):
1128 expected_class = datetime
1129 else:
1130 expected_class = date
1131 self.assertIsInstance(self.theclass.min, expected_class)
1132 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001133 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001134 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001135
1136 def test_extreme_timedelta(self):
1137 big = self.theclass.max - self.theclass.min
1138 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1139 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1140 # n == 315537897599999999 ~= 2**58.13
1141 justasbig = timedelta(0, 0, n)
1142 self.assertEqual(big, justasbig)
1143 self.assertEqual(self.theclass.min + big, self.theclass.max)
1144 self.assertEqual(self.theclass.max - big, self.theclass.min)
1145
1146 def test_timetuple(self):
1147 for i in range(7):
1148 # January 2, 1956 is a Monday (0)
1149 d = self.theclass(1956, 1, 2+i)
1150 t = d.timetuple()
1151 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1152 # February 1, 1956 is a Wednesday (2)
1153 d = self.theclass(1956, 2, 1+i)
1154 t = d.timetuple()
1155 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1156 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1157 # of the year.
1158 d = self.theclass(1956, 3, 1+i)
1159 t = d.timetuple()
1160 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1161 self.assertEqual(t.tm_year, 1956)
1162 self.assertEqual(t.tm_mon, 3)
1163 self.assertEqual(t.tm_mday, 1+i)
1164 self.assertEqual(t.tm_hour, 0)
1165 self.assertEqual(t.tm_min, 0)
1166 self.assertEqual(t.tm_sec, 0)
1167 self.assertEqual(t.tm_wday, (3+i)%7)
1168 self.assertEqual(t.tm_yday, 61+i)
1169 self.assertEqual(t.tm_isdst, -1)
1170
1171 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001172 args = 6, 7, 23
1173 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001174 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001175 green = pickler.dumps(orig, proto)
1176 derived = unpickler.loads(green)
1177 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001178
1179 def test_compare(self):
1180 t1 = self.theclass(2, 3, 4)
1181 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001182 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001183 self.assertTrue(t1 <= t2)
1184 self.assertTrue(t1 >= t2)
1185 self.assertTrue(not t1 != t2)
1186 self.assertTrue(not t1 < t2)
1187 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001188
1189 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1190 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001191 self.assertTrue(t1 < t2)
1192 self.assertTrue(t2 > t1)
1193 self.assertTrue(t1 <= t2)
1194 self.assertTrue(t2 >= t1)
1195 self.assertTrue(t1 != t2)
1196 self.assertTrue(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)
1201 self.assertTrue(not t1 >= t2)
1202 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001203
Tim Peters68124bb2003-02-08 03:46:31 +00001204 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001205 self.assertEqual(t1 == badarg, False)
1206 self.assertEqual(t1 != badarg, True)
1207 self.assertEqual(badarg == t1, False)
1208 self.assertEqual(badarg != t1, True)
1209
Tim Peters2a799bf2002-12-16 20:18:38 +00001210 self.assertRaises(TypeError, lambda: t1 < badarg)
1211 self.assertRaises(TypeError, lambda: t1 > badarg)
1212 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001213 self.assertRaises(TypeError, lambda: badarg <= t1)
1214 self.assertRaises(TypeError, lambda: badarg < t1)
1215 self.assertRaises(TypeError, lambda: badarg > t1)
1216 self.assertRaises(TypeError, lambda: badarg >= t1)
1217
Tim Peters8d81a012003-01-24 22:36:34 +00001218 def test_mixed_compare(self):
1219 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001220
1221 # Our class can be compared for equality to other classes
1222 self.assertEqual(our == 1, False)
1223 self.assertEqual(1 == our, False)
1224 self.assertEqual(our != 1, True)
1225 self.assertEqual(1 != our, True)
1226
1227 # But the ordering is undefined
1228 self.assertRaises(TypeError, lambda: our < 1)
1229 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001230
Guido van Rossum19960592006-08-24 17:29:38 +00001231 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001232
Guido van Rossum19960592006-08-24 17:29:38 +00001233 class SomeClass:
1234 pass
1235
1236 their = SomeClass()
1237 self.assertEqual(our == their, False)
1238 self.assertEqual(their == our, False)
1239 self.assertEqual(our != their, True)
1240 self.assertEqual(their != our, True)
1241 self.assertRaises(TypeError, lambda: our < their)
1242 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001243
Guido van Rossum19960592006-08-24 17:29:38 +00001244 # However, if the other class explicitly defines ordering
1245 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001246
Guido van Rossum19960592006-08-24 17:29:38 +00001247 class LargerThanAnything:
1248 def __lt__(self, other):
1249 return False
1250 def __le__(self, other):
1251 return isinstance(other, LargerThanAnything)
1252 def __eq__(self, other):
1253 return isinstance(other, LargerThanAnything)
1254 def __ne__(self, other):
1255 return not isinstance(other, LargerThanAnything)
1256 def __gt__(self, other):
1257 return not isinstance(other, LargerThanAnything)
1258 def __ge__(self, other):
1259 return True
1260
1261 their = LargerThanAnything()
1262 self.assertEqual(our == their, False)
1263 self.assertEqual(their == our, False)
1264 self.assertEqual(our != their, True)
1265 self.assertEqual(their != our, True)
1266 self.assertEqual(our < their, True)
1267 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001268
Tim Peters2a799bf2002-12-16 20:18:38 +00001269 def test_bool(self):
1270 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001271 self.assertTrue(self.theclass.min)
1272 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001273
Guido van Rossum04110fb2007-08-24 16:32:05 +00001274 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001275 # For nasty technical reasons, we can't handle years before 1900.
1276 cls = self.theclass
1277 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1278 for y in 1, 49, 51, 99, 100, 1000, 1899:
1279 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001280
1281 def test_replace(self):
1282 cls = self.theclass
1283 args = [1, 2, 3]
1284 base = cls(*args)
1285 self.assertEqual(base, base.replace())
1286
1287 i = 0
1288 for name, newval in (("year", 2),
1289 ("month", 3),
1290 ("day", 4)):
1291 newargs = args[:]
1292 newargs[i] = newval
1293 expected = cls(*newargs)
1294 got = base.replace(**{name: newval})
1295 self.assertEqual(expected, got)
1296 i += 1
1297
1298 # Out of bounds.
1299 base = cls(2000, 2, 29)
1300 self.assertRaises(ValueError, base.replace, year=2001)
1301
Tim Petersa98924a2003-05-17 05:55:19 +00001302 def test_subclass_date(self):
1303
1304 class C(self.theclass):
1305 theAnswer = 42
1306
1307 def __new__(cls, *args, **kws):
1308 temp = kws.copy()
1309 extra = temp.pop('extra')
1310 result = self.theclass.__new__(cls, *args, **temp)
1311 result.extra = extra
1312 return result
1313
1314 def newmeth(self, start):
1315 return start + self.year + self.month
1316
1317 args = 2003, 4, 14
1318
1319 dt1 = self.theclass(*args)
1320 dt2 = C(*args, **{'extra': 7})
1321
1322 self.assertEqual(dt2.__class__, C)
1323 self.assertEqual(dt2.theAnswer, 42)
1324 self.assertEqual(dt2.extra, 7)
1325 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1326 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1327
Tim Peters604c0132004-06-07 23:04:33 +00001328 def test_pickling_subclass_date(self):
1329
1330 args = 6, 7, 23
1331 orig = SubclassDate(*args)
1332 for pickler, unpickler, proto in pickle_choices:
1333 green = pickler.dumps(orig, proto)
1334 derived = unpickler.loads(green)
1335 self.assertEqual(orig, derived)
1336
Tim Peters3f606292004-03-21 23:38:41 +00001337 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001338 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001339 # This is a low-overhead backdoor. A user can (by intent or
1340 # mistake) pass a string directly, which (if it's the right length)
1341 # will get treated like a pickle, and bypass the normal sanity
1342 # checks in the constructor. This can create insane objects.
1343 # The constructor doesn't want to burn the time to validate all
1344 # fields, but does check the month field. This stops, e.g.,
1345 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001346 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001347 if not issubclass(self.theclass, datetime):
1348 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001349 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001350 self.assertRaises(TypeError, self.theclass,
1351 base[:2] + month_byte + base[3:])
Alexander Belopolskyd87e9322010-07-05 17:57:31 +00001352 # Good bytes, but bad tzinfo:
1353 self.assertRaises(TypeError, self.theclass,
1354 bytes([1] * len(base)), 'EST')
1355
Tim Peters3f606292004-03-21 23:38:41 +00001356 for ord_byte in range(1, 13):
1357 # This shouldn't blow up because of the month byte alone. If
1358 # the implementation changes to do more-careful checking, it may
1359 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001360 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001361
Tim Peters2a799bf2002-12-16 20:18:38 +00001362#############################################################################
1363# datetime tests
1364
Tim Peters604c0132004-06-07 23:04:33 +00001365class SubclassDatetime(datetime):
1366 sub_var = 1
1367
Tim Peters2a799bf2002-12-16 20:18:38 +00001368class TestDateTime(TestDate):
1369
1370 theclass = datetime
1371
1372 def test_basic_attributes(self):
1373 dt = self.theclass(2002, 3, 1, 12, 0)
1374 self.assertEqual(dt.year, 2002)
1375 self.assertEqual(dt.month, 3)
1376 self.assertEqual(dt.day, 1)
1377 self.assertEqual(dt.hour, 12)
1378 self.assertEqual(dt.minute, 0)
1379 self.assertEqual(dt.second, 0)
1380 self.assertEqual(dt.microsecond, 0)
1381
1382 def test_basic_attributes_nonzero(self):
1383 # Make sure all attributes are non-zero so bugs in
1384 # bit-shifting access show up.
1385 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1386 self.assertEqual(dt.year, 2002)
1387 self.assertEqual(dt.month, 3)
1388 self.assertEqual(dt.day, 1)
1389 self.assertEqual(dt.hour, 12)
1390 self.assertEqual(dt.minute, 59)
1391 self.assertEqual(dt.second, 59)
1392 self.assertEqual(dt.microsecond, 8000)
1393
1394 def test_roundtrip(self):
1395 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1396 self.theclass.now()):
1397 # Verify dt -> string -> datetime identity.
1398 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001399 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001400 s = s[9:]
1401 dt2 = eval(s)
1402 self.assertEqual(dt, dt2)
1403
1404 # Verify identity via reconstructing from pieces.
1405 dt2 = self.theclass(dt.year, dt.month, dt.day,
1406 dt.hour, dt.minute, dt.second,
1407 dt.microsecond)
1408 self.assertEqual(dt, dt2)
1409
1410 def test_isoformat(self):
1411 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1412 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1413 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1414 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001415 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001416 # str is ISO format with the separator forced to a blank.
1417 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1418
1419 t = self.theclass(2, 3, 2)
1420 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1421 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1422 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1423 # str is ISO format with the separator forced to a blank.
1424 self.assertEqual(str(t), "0002-03-02 00:00:00")
1425
Eric Smith1ba31142007-09-11 18:06:02 +00001426 def test_format(self):
1427 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001428 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001429
1430 # check that a derived class's __str__() gets called
1431 class A(self.theclass):
1432 def __str__(self):
1433 return 'A'
1434 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001435 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001436
1437 # check that a derived class's strftime gets called
1438 class B(self.theclass):
1439 def strftime(self, format_spec):
1440 return 'B'
1441 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001442 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001443
1444 for fmt in ["m:%m d:%d y:%y",
1445 "m:%m d:%d y:%y H:%H M:%M S:%S",
1446 "%z %Z",
1447 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001448 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1449 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1450 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001451
Tim Peters2a799bf2002-12-16 20:18:38 +00001452 def test_more_ctime(self):
1453 # Test fields that TestDate doesn't touch.
1454 import time
1455
1456 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1457 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1458 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1459 # out. The difference is that t.ctime() produces " 2" for the day,
1460 # but platform ctime() produces "02" for the day. According to
1461 # C99, t.ctime() is correct here.
1462 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1463
1464 # So test a case where that difference doesn't matter.
1465 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1466 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1467
1468 def test_tz_independent_comparing(self):
1469 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1470 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1471 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1472 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001473 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001474
1475 # Make sure comparison doesn't forget microseconds, and isn't done
1476 # via comparing a float timestamp (an IEEE double doesn't have enough
1477 # precision to span microsecond resolution across years 1 thru 9999,
1478 # so comparing via timestamp necessarily calls some distinct values
1479 # equal).
1480 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1481 us = timedelta(microseconds=1)
1482 dt2 = dt1 + us
1483 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001484 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001485
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001486 def test_strftime_with_bad_tzname_replace(self):
1487 # verify ok if tzinfo.tzname().replace() returns a non-string
1488 class MyTzInfo(FixedOffset):
1489 def tzname(self, dt):
1490 class MyStr(str):
1491 def replace(self, *args):
1492 return None
1493 return MyStr('name')
1494 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1495 self.assertRaises(TypeError, t.strftime, '%Z')
1496
Tim Peters2a799bf2002-12-16 20:18:38 +00001497 def test_bad_constructor_arguments(self):
1498 # bad years
1499 self.theclass(MINYEAR, 1, 1) # no exception
1500 self.theclass(MAXYEAR, 1, 1) # no exception
1501 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1502 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1503 # bad months
1504 self.theclass(2000, 1, 1) # no exception
1505 self.theclass(2000, 12, 1) # no exception
1506 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1507 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1508 # bad days
1509 self.theclass(2000, 2, 29) # no exception
1510 self.theclass(2004, 2, 29) # no exception
1511 self.theclass(2400, 2, 29) # no exception
1512 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1513 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1514 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1515 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1516 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1517 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1518 # bad hours
1519 self.theclass(2000, 1, 31, 0) # no exception
1520 self.theclass(2000, 1, 31, 23) # no exception
1521 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1522 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1523 # bad minutes
1524 self.theclass(2000, 1, 31, 23, 0) # no exception
1525 self.theclass(2000, 1, 31, 23, 59) # no exception
1526 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1527 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1528 # bad seconds
1529 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1530 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1531 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1532 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1533 # bad microseconds
1534 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1535 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1536 self.assertRaises(ValueError, self.theclass,
1537 2000, 1, 31, 23, 59, 59, -1)
1538 self.assertRaises(ValueError, self.theclass,
1539 2000, 1, 31, 23, 59, 59,
1540 1000000)
1541
1542 def test_hash_equality(self):
1543 d = self.theclass(2000, 12, 31, 23, 30, 17)
1544 e = self.theclass(2000, 12, 31, 23, 30, 17)
1545 self.assertEqual(d, e)
1546 self.assertEqual(hash(d), hash(e))
1547
1548 dic = {d: 1}
1549 dic[e] = 2
1550 self.assertEqual(len(dic), 1)
1551 self.assertEqual(dic[d], 2)
1552 self.assertEqual(dic[e], 2)
1553
1554 d = self.theclass(2001, 1, 1, 0, 5, 17)
1555 e = self.theclass(2001, 1, 1, 0, 5, 17)
1556 self.assertEqual(d, e)
1557 self.assertEqual(hash(d), hash(e))
1558
1559 dic = {d: 1}
1560 dic[e] = 2
1561 self.assertEqual(len(dic), 1)
1562 self.assertEqual(dic[d], 2)
1563 self.assertEqual(dic[e], 2)
1564
1565 def test_computations(self):
1566 a = self.theclass(2002, 1, 31)
1567 b = self.theclass(1956, 1, 31)
1568 diff = a-b
1569 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1570 self.assertEqual(diff.seconds, 0)
1571 self.assertEqual(diff.microseconds, 0)
1572 a = self.theclass(2002, 3, 2, 17, 6)
1573 millisec = timedelta(0, 0, 1000)
1574 hour = timedelta(0, 3600)
1575 day = timedelta(1)
1576 week = timedelta(7)
1577 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1578 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1579 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1580 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1581 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1582 self.assertEqual(a - hour, a + -hour)
1583 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1584 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1585 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1586 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1587 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1588 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1589 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1590 self.assertEqual((a + week) - a, week)
1591 self.assertEqual((a + day) - a, day)
1592 self.assertEqual((a + hour) - a, hour)
1593 self.assertEqual((a + millisec) - a, millisec)
1594 self.assertEqual((a - week) - a, -week)
1595 self.assertEqual((a - day) - a, -day)
1596 self.assertEqual((a - hour) - a, -hour)
1597 self.assertEqual((a - millisec) - a, -millisec)
1598 self.assertEqual(a - (a + week), -week)
1599 self.assertEqual(a - (a + day), -day)
1600 self.assertEqual(a - (a + hour), -hour)
1601 self.assertEqual(a - (a + millisec), -millisec)
1602 self.assertEqual(a - (a - week), week)
1603 self.assertEqual(a - (a - day), day)
1604 self.assertEqual(a - (a - hour), hour)
1605 self.assertEqual(a - (a - millisec), millisec)
1606 self.assertEqual(a + (week + day + hour + millisec),
1607 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1608 self.assertEqual(a + (week + day + hour + millisec),
1609 (((a + week) + day) + hour) + millisec)
1610 self.assertEqual(a - (week + day + hour + millisec),
1611 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1612 self.assertEqual(a - (week + day + hour + millisec),
1613 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001614 # Add/sub ints or floats should be illegal
1615 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001616 self.assertRaises(TypeError, lambda: a+i)
1617 self.assertRaises(TypeError, lambda: a-i)
1618 self.assertRaises(TypeError, lambda: i+a)
1619 self.assertRaises(TypeError, lambda: i-a)
1620
1621 # delta - datetime is senseless.
1622 self.assertRaises(TypeError, lambda: day - a)
1623 # mixing datetime and (delta or datetime) via * or // is senseless
1624 self.assertRaises(TypeError, lambda: day * a)
1625 self.assertRaises(TypeError, lambda: a * day)
1626 self.assertRaises(TypeError, lambda: day // a)
1627 self.assertRaises(TypeError, lambda: a // day)
1628 self.assertRaises(TypeError, lambda: a * a)
1629 self.assertRaises(TypeError, lambda: a // a)
1630 # datetime + datetime is senseless
1631 self.assertRaises(TypeError, lambda: a + a)
1632
1633 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001634 args = 6, 7, 23, 20, 59, 1, 64**2
1635 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001636 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001637 green = pickler.dumps(orig, proto)
1638 derived = unpickler.loads(green)
1639 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001640
Guido van Rossum275666f2003-02-07 21:49:01 +00001641 def test_more_pickling(self):
1642 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1643 s = pickle.dumps(a)
1644 b = pickle.loads(s)
1645 self.assertEqual(b.year, 2003)
1646 self.assertEqual(b.month, 2)
1647 self.assertEqual(b.day, 7)
1648
Tim Peters604c0132004-06-07 23:04:33 +00001649 def test_pickling_subclass_datetime(self):
1650 args = 6, 7, 23, 20, 59, 1, 64**2
1651 orig = SubclassDatetime(*args)
1652 for pickler, unpickler, proto in pickle_choices:
1653 green = pickler.dumps(orig, proto)
1654 derived = unpickler.loads(green)
1655 self.assertEqual(orig, derived)
1656
Tim Peters2a799bf2002-12-16 20:18:38 +00001657 def test_more_compare(self):
1658 # The test_compare() inherited from TestDate covers the error cases.
1659 # We just want to test lexicographic ordering on the members datetime
1660 # has that date lacks.
1661 args = [2000, 11, 29, 20, 58, 16, 999998]
1662 t1 = self.theclass(*args)
1663 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001664 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001665 self.assertTrue(t1 <= t2)
1666 self.assertTrue(t1 >= t2)
1667 self.assertTrue(not t1 != t2)
1668 self.assertTrue(not t1 < t2)
1669 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001670
1671 for i in range(len(args)):
1672 newargs = args[:]
1673 newargs[i] = args[i] + 1
1674 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001675 self.assertTrue(t1 < t2)
1676 self.assertTrue(t2 > t1)
1677 self.assertTrue(t1 <= t2)
1678 self.assertTrue(t2 >= t1)
1679 self.assertTrue(t1 != t2)
1680 self.assertTrue(t2 != t1)
1681 self.assertTrue(not t1 == t2)
1682 self.assertTrue(not t2 == t1)
1683 self.assertTrue(not t1 > t2)
1684 self.assertTrue(not t2 < t1)
1685 self.assertTrue(not t1 >= t2)
1686 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001687
1688
1689 # A helper for timestamp constructor tests.
1690 def verify_field_equality(self, expected, got):
1691 self.assertEqual(expected.tm_year, got.year)
1692 self.assertEqual(expected.tm_mon, got.month)
1693 self.assertEqual(expected.tm_mday, got.day)
1694 self.assertEqual(expected.tm_hour, got.hour)
1695 self.assertEqual(expected.tm_min, got.minute)
1696 self.assertEqual(expected.tm_sec, got.second)
1697
1698 def test_fromtimestamp(self):
1699 import time
1700
1701 ts = time.time()
1702 expected = time.localtime(ts)
1703 got = self.theclass.fromtimestamp(ts)
1704 self.verify_field_equality(expected, got)
1705
1706 def test_utcfromtimestamp(self):
1707 import time
1708
1709 ts = time.time()
1710 expected = time.gmtime(ts)
1711 got = self.theclass.utcfromtimestamp(ts)
1712 self.verify_field_equality(expected, got)
1713
Thomas Wouters477c8d52006-05-27 19:21:47 +00001714 def test_microsecond_rounding(self):
1715 # Test whether fromtimestamp "rounds up" floats that are less
1716 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001717 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1718 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001719
Tim Peters1b6f7a92004-06-20 02:50:16 +00001720 def test_insane_fromtimestamp(self):
1721 # It's possible that some platform maps time_t to double,
1722 # and that this test will fail there. This test should
1723 # exempt such platforms (provided they return reasonable
1724 # results!).
1725 for insane in -1e200, 1e200:
1726 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1727 insane)
1728
1729 def test_insane_utcfromtimestamp(self):
1730 # It's possible that some platform maps time_t to double,
1731 # and that this test will fail there. This test should
1732 # exempt such platforms (provided they return reasonable
1733 # results!).
1734 for insane in -1e200, 1e200:
1735 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1736 insane)
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_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001739 # The result is tz-dependent; at least test that this doesn't
1740 # fail (like it did before bug 1646728 was fixed).
1741 self.theclass.fromtimestamp(-1.05)
1742
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001743 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001744 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001745 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001746 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001747
Tim Peters2a799bf2002-12-16 20:18:38 +00001748 def test_utcnow(self):
1749 import time
1750
1751 # Call it a success if utcnow() and utcfromtimestamp() are within
1752 # a second of each other.
1753 tolerance = timedelta(seconds=1)
1754 for dummy in range(3):
1755 from_now = self.theclass.utcnow()
1756 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1757 if abs(from_timestamp - from_now) <= tolerance:
1758 break
1759 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001760 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001761
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001762 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001763 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001764
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001765 string = '2004-12-01 13:02:47.197'
1766 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001767 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001768 got = self.theclass.strptime(string, format)
1769 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001770 self.assertIs(type(expected), self.theclass)
1771 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001772
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001773 strptime = self.theclass.strptime
1774 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1775 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1776 # Only local timezone and UTC are supported
1777 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1778 (-_time.timezone, _time.tzname[0])):
1779 if tzseconds < 0:
1780 sign = '-'
1781 seconds = -tzseconds
1782 else:
1783 sign ='+'
1784 seconds = tzseconds
1785 hours, minutes = divmod(seconds//60, 60)
1786 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1787 dt = strptime(dtstr, "%z %Z")
1788 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1789 self.assertEqual(dt.tzname(), tzname)
1790 # Can produce inconsistent datetime
1791 dtstr, fmt = "+1234 UTC", "%z %Z"
1792 dt = strptime(dtstr, fmt)
1793 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1794 self.assertEqual(dt.tzname(), 'UTC')
1795 # yet will roundtrip
1796 self.assertEqual(dt.strftime(fmt), dtstr)
1797
1798 # Produce naive datetime if no %z is provided
1799 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1800
1801 with self.assertRaises(ValueError): strptime("-2400", "%z")
1802 with self.assertRaises(ValueError): strptime("-000", "%z")
1803
Tim Peters2a799bf2002-12-16 20:18:38 +00001804 def test_more_timetuple(self):
1805 # This tests fields beyond those tested by the TestDate.test_timetuple.
1806 t = self.theclass(2004, 12, 31, 6, 22, 33)
1807 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1808 self.assertEqual(t.timetuple(),
1809 (t.year, t.month, t.day,
1810 t.hour, t.minute, t.second,
1811 t.weekday(),
1812 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1813 -1))
1814 tt = t.timetuple()
1815 self.assertEqual(tt.tm_year, t.year)
1816 self.assertEqual(tt.tm_mon, t.month)
1817 self.assertEqual(tt.tm_mday, t.day)
1818 self.assertEqual(tt.tm_hour, t.hour)
1819 self.assertEqual(tt.tm_min, t.minute)
1820 self.assertEqual(tt.tm_sec, t.second)
1821 self.assertEqual(tt.tm_wday, t.weekday())
1822 self.assertEqual(tt.tm_yday, t.toordinal() -
1823 date(t.year, 1, 1).toordinal() + 1)
1824 self.assertEqual(tt.tm_isdst, -1)
1825
1826 def test_more_strftime(self):
1827 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001828 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1829 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1830 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001831
1832 def test_extract(self):
1833 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1834 self.assertEqual(dt.date(), date(2002, 3, 4))
1835 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1836
1837 def test_combine(self):
1838 d = date(2002, 3, 4)
1839 t = time(18, 45, 3, 1234)
1840 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1841 combine = self.theclass.combine
1842 dt = combine(d, t)
1843 self.assertEqual(dt, expected)
1844
1845 dt = combine(time=t, date=d)
1846 self.assertEqual(dt, expected)
1847
1848 self.assertEqual(d, dt.date())
1849 self.assertEqual(t, dt.time())
1850 self.assertEqual(dt, combine(dt.date(), dt.time()))
1851
1852 self.assertRaises(TypeError, combine) # need an arg
1853 self.assertRaises(TypeError, combine, d) # need two args
1854 self.assertRaises(TypeError, combine, t, d) # args reversed
1855 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1856 self.assertRaises(TypeError, combine, "date", "time") # wrong types
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00001857 self.assertRaises(TypeError, combine, d, "time") # wrong type
1858 self.assertRaises(TypeError, combine, "date", t) # wrong type
Tim Peters2a799bf2002-12-16 20:18:38 +00001859
Tim Peters12bf3392002-12-24 05:41:27 +00001860 def test_replace(self):
1861 cls = self.theclass
1862 args = [1, 2, 3, 4, 5, 6, 7]
1863 base = cls(*args)
1864 self.assertEqual(base, base.replace())
1865
1866 i = 0
1867 for name, newval in (("year", 2),
1868 ("month", 3),
1869 ("day", 4),
1870 ("hour", 5),
1871 ("minute", 6),
1872 ("second", 7),
1873 ("microsecond", 8)):
1874 newargs = args[:]
1875 newargs[i] = newval
1876 expected = cls(*newargs)
1877 got = base.replace(**{name: newval})
1878 self.assertEqual(expected, got)
1879 i += 1
1880
1881 # Out of bounds.
1882 base = cls(2000, 2, 29)
1883 self.assertRaises(ValueError, base.replace, year=2001)
1884
Tim Peters80475bb2002-12-25 07:40:55 +00001885 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001886 # Pretty boring! The TZ test is more interesting here. astimezone()
1887 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001888 dt = self.theclass.now()
1889 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001890 self.assertRaises(TypeError, dt.astimezone) # not enough args
1891 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1892 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001893 self.assertRaises(ValueError, dt.astimezone, f) # naive
1894 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001895
Tim Peters52dcce22003-01-23 16:36:11 +00001896 class Bogus(tzinfo):
1897 def utcoffset(self, dt): return None
1898 def dst(self, dt): return timedelta(0)
1899 bog = Bogus()
1900 self.assertRaises(ValueError, dt.astimezone, bog) # naive
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00001901 self.assertRaises(ValueError,
1902 dt.replace(tzinfo=bog).astimezone, f)
Tim Peters52dcce22003-01-23 16:36:11 +00001903
1904 class AlsoBogus(tzinfo):
1905 def utcoffset(self, dt): return timedelta(0)
1906 def dst(self, dt): return None
1907 alsobog = AlsoBogus()
1908 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001909
Tim Petersa98924a2003-05-17 05:55:19 +00001910 def test_subclass_datetime(self):
1911
1912 class C(self.theclass):
1913 theAnswer = 42
1914
1915 def __new__(cls, *args, **kws):
1916 temp = kws.copy()
1917 extra = temp.pop('extra')
1918 result = self.theclass.__new__(cls, *args, **temp)
1919 result.extra = extra
1920 return result
1921
1922 def newmeth(self, start):
1923 return start + self.year + self.month + self.second
1924
1925 args = 2003, 4, 14, 12, 13, 41
1926
1927 dt1 = self.theclass(*args)
1928 dt2 = C(*args, **{'extra': 7})
1929
1930 self.assertEqual(dt2.__class__, C)
1931 self.assertEqual(dt2.theAnswer, 42)
1932 self.assertEqual(dt2.extra, 7)
1933 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1934 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1935 dt1.second - 7)
1936
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001937class TestSubclassDateTime(TestDateTime):
1938 theclass = SubclassDatetime
1939 # Override tests not designed for subclass
1940 def test_roundtrip(self):
1941 pass
1942
Tim Peters604c0132004-06-07 23:04:33 +00001943class SubclassTime(time):
1944 sub_var = 1
1945
Guido van Rossumd8faa362007-04-27 19:54:29 +00001946class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001947
1948 theclass = time
1949
1950 def test_basic_attributes(self):
1951 t = self.theclass(12, 0)
1952 self.assertEqual(t.hour, 12)
1953 self.assertEqual(t.minute, 0)
1954 self.assertEqual(t.second, 0)
1955 self.assertEqual(t.microsecond, 0)
1956
1957 def test_basic_attributes_nonzero(self):
1958 # Make sure all attributes are non-zero so bugs in
1959 # bit-shifting access show up.
1960 t = self.theclass(12, 59, 59, 8000)
1961 self.assertEqual(t.hour, 12)
1962 self.assertEqual(t.minute, 59)
1963 self.assertEqual(t.second, 59)
1964 self.assertEqual(t.microsecond, 8000)
1965
1966 def test_roundtrip(self):
1967 t = self.theclass(1, 2, 3, 4)
1968
1969 # Verify t -> string -> time identity.
1970 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001971 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001972 s = s[9:]
1973 t2 = eval(s)
1974 self.assertEqual(t, t2)
1975
1976 # Verify identity via reconstructing from pieces.
1977 t2 = self.theclass(t.hour, t.minute, t.second,
1978 t.microsecond)
1979 self.assertEqual(t, t2)
1980
1981 def test_comparing(self):
1982 args = [1, 2, 3, 4]
1983 t1 = self.theclass(*args)
1984 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001985 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001986 self.assertTrue(t1 <= t2)
1987 self.assertTrue(t1 >= t2)
1988 self.assertTrue(not t1 != t2)
1989 self.assertTrue(not t1 < t2)
1990 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001991
1992 for i in range(len(args)):
1993 newargs = args[:]
1994 newargs[i] = args[i] + 1
1995 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001996 self.assertTrue(t1 < t2)
1997 self.assertTrue(t2 > t1)
1998 self.assertTrue(t1 <= t2)
1999 self.assertTrue(t2 >= t1)
2000 self.assertTrue(t1 != t2)
2001 self.assertTrue(t2 != t1)
2002 self.assertTrue(not t1 == t2)
2003 self.assertTrue(not t2 == t1)
2004 self.assertTrue(not t1 > t2)
2005 self.assertTrue(not t2 < t1)
2006 self.assertTrue(not t1 >= t2)
2007 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002008
Tim Peters68124bb2003-02-08 03:46:31 +00002009 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00002010 self.assertEqual(t1 == badarg, False)
2011 self.assertEqual(t1 != badarg, True)
2012 self.assertEqual(badarg == t1, False)
2013 self.assertEqual(badarg != t1, True)
2014
Tim Peters2a799bf2002-12-16 20:18:38 +00002015 self.assertRaises(TypeError, lambda: t1 <= badarg)
2016 self.assertRaises(TypeError, lambda: t1 < badarg)
2017 self.assertRaises(TypeError, lambda: t1 > badarg)
2018 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00002019 self.assertRaises(TypeError, lambda: badarg <= t1)
2020 self.assertRaises(TypeError, lambda: badarg < t1)
2021 self.assertRaises(TypeError, lambda: badarg > t1)
2022 self.assertRaises(TypeError, lambda: badarg >= t1)
2023
2024 def test_bad_constructor_arguments(self):
2025 # bad hours
2026 self.theclass(0, 0) # no exception
2027 self.theclass(23, 0) # no exception
2028 self.assertRaises(ValueError, self.theclass, -1, 0)
2029 self.assertRaises(ValueError, self.theclass, 24, 0)
2030 # bad minutes
2031 self.theclass(23, 0) # no exception
2032 self.theclass(23, 59) # no exception
2033 self.assertRaises(ValueError, self.theclass, 23, -1)
2034 self.assertRaises(ValueError, self.theclass, 23, 60)
2035 # bad seconds
2036 self.theclass(23, 59, 0) # no exception
2037 self.theclass(23, 59, 59) # no exception
2038 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2039 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2040 # bad microseconds
2041 self.theclass(23, 59, 59, 0) # no exception
2042 self.theclass(23, 59, 59, 999999) # no exception
2043 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2044 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2045
2046 def test_hash_equality(self):
2047 d = self.theclass(23, 30, 17)
2048 e = self.theclass(23, 30, 17)
2049 self.assertEqual(d, e)
2050 self.assertEqual(hash(d), hash(e))
2051
2052 dic = {d: 1}
2053 dic[e] = 2
2054 self.assertEqual(len(dic), 1)
2055 self.assertEqual(dic[d], 2)
2056 self.assertEqual(dic[e], 2)
2057
2058 d = self.theclass(0, 5, 17)
2059 e = self.theclass(0, 5, 17)
2060 self.assertEqual(d, e)
2061 self.assertEqual(hash(d), hash(e))
2062
2063 dic = {d: 1}
2064 dic[e] = 2
2065 self.assertEqual(len(dic), 1)
2066 self.assertEqual(dic[d], 2)
2067 self.assertEqual(dic[e], 2)
2068
2069 def test_isoformat(self):
2070 t = self.theclass(4, 5, 1, 123)
2071 self.assertEqual(t.isoformat(), "04:05:01.000123")
2072 self.assertEqual(t.isoformat(), str(t))
2073
2074 t = self.theclass()
2075 self.assertEqual(t.isoformat(), "00:00:00")
2076 self.assertEqual(t.isoformat(), str(t))
2077
2078 t = self.theclass(microsecond=1)
2079 self.assertEqual(t.isoformat(), "00:00:00.000001")
2080 self.assertEqual(t.isoformat(), str(t))
2081
2082 t = self.theclass(microsecond=10)
2083 self.assertEqual(t.isoformat(), "00:00:00.000010")
2084 self.assertEqual(t.isoformat(), str(t))
2085
2086 t = self.theclass(microsecond=100)
2087 self.assertEqual(t.isoformat(), "00:00:00.000100")
2088 self.assertEqual(t.isoformat(), str(t))
2089
2090 t = self.theclass(microsecond=1000)
2091 self.assertEqual(t.isoformat(), "00:00:00.001000")
2092 self.assertEqual(t.isoformat(), str(t))
2093
2094 t = self.theclass(microsecond=10000)
2095 self.assertEqual(t.isoformat(), "00:00:00.010000")
2096 self.assertEqual(t.isoformat(), str(t))
2097
2098 t = self.theclass(microsecond=100000)
2099 self.assertEqual(t.isoformat(), "00:00:00.100000")
2100 self.assertEqual(t.isoformat(), str(t))
2101
Thomas Wouterscf297e42007-02-23 15:07:44 +00002102 def test_1653736(self):
2103 # verify it doesn't accept extra keyword arguments
2104 t = self.theclass(second=1)
2105 self.assertRaises(TypeError, t.isoformat, foo=3)
2106
Tim Peters2a799bf2002-12-16 20:18:38 +00002107 def test_strftime(self):
2108 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002109 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002110 # A naive object replaces %z and %Z with empty strings.
2111 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2112
Eric Smith1ba31142007-09-11 18:06:02 +00002113 def test_format(self):
2114 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002115 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002116
2117 # check that a derived class's __str__() gets called
2118 class A(self.theclass):
2119 def __str__(self):
2120 return 'A'
2121 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002122 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002123
2124 # check that a derived class's strftime gets called
2125 class B(self.theclass):
2126 def strftime(self, format_spec):
2127 return 'B'
2128 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002129 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002130
2131 for fmt in ['%H %M %S',
2132 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002133 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2134 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2135 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002136
Tim Peters2a799bf2002-12-16 20:18:38 +00002137 def test_str(self):
2138 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2139 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2140 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2141 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2142 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2143
2144 def test_repr(self):
2145 name = 'datetime.' + self.theclass.__name__
2146 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2147 "%s(1, 2, 3, 4)" % name)
2148 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2149 "%s(10, 2, 3, 4000)" % name)
2150 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2151 "%s(0, 2, 3, 400000)" % name)
2152 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2153 "%s(12, 2, 3)" % name)
2154 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2155 "%s(23, 15)" % name)
2156
2157 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002158 self.assertIsInstance(self.theclass.min, self.theclass)
2159 self.assertIsInstance(self.theclass.max, self.theclass)
2160 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002161 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002162
2163 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002164 args = 20, 59, 16, 64**2
2165 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002166 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002167 green = pickler.dumps(orig, proto)
2168 derived = unpickler.loads(green)
2169 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002170
Tim Peters604c0132004-06-07 23:04:33 +00002171 def test_pickling_subclass_time(self):
2172 args = 20, 59, 16, 64**2
2173 orig = SubclassTime(*args)
2174 for pickler, unpickler, proto in pickle_choices:
2175 green = pickler.dumps(orig, proto)
2176 derived = unpickler.loads(green)
2177 self.assertEqual(orig, derived)
2178
Tim Peters2a799bf2002-12-16 20:18:38 +00002179 def test_bool(self):
2180 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002181 self.assertTrue(cls(1))
2182 self.assertTrue(cls(0, 1))
2183 self.assertTrue(cls(0, 0, 1))
2184 self.assertTrue(cls(0, 0, 0, 1))
2185 self.assertTrue(not cls(0))
2186 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002187
Tim Peters12bf3392002-12-24 05:41:27 +00002188 def test_replace(self):
2189 cls = self.theclass
2190 args = [1, 2, 3, 4]
2191 base = cls(*args)
2192 self.assertEqual(base, base.replace())
2193
2194 i = 0
2195 for name, newval in (("hour", 5),
2196 ("minute", 6),
2197 ("second", 7),
2198 ("microsecond", 8)):
2199 newargs = args[:]
2200 newargs[i] = newval
2201 expected = cls(*newargs)
2202 got = base.replace(**{name: newval})
2203 self.assertEqual(expected, got)
2204 i += 1
2205
2206 # Out of bounds.
2207 base = cls(1)
2208 self.assertRaises(ValueError, base.replace, hour=24)
2209 self.assertRaises(ValueError, base.replace, minute=-1)
2210 self.assertRaises(ValueError, base.replace, second=100)
2211 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2212
Tim Petersa98924a2003-05-17 05:55:19 +00002213 def test_subclass_time(self):
2214
2215 class C(self.theclass):
2216 theAnswer = 42
2217
2218 def __new__(cls, *args, **kws):
2219 temp = kws.copy()
2220 extra = temp.pop('extra')
2221 result = self.theclass.__new__(cls, *args, **temp)
2222 result.extra = extra
2223 return result
2224
2225 def newmeth(self, start):
2226 return start + self.hour + self.second
2227
2228 args = 4, 5, 6
2229
2230 dt1 = self.theclass(*args)
2231 dt2 = C(*args, **{'extra': 7})
2232
2233 self.assertEqual(dt2.__class__, C)
2234 self.assertEqual(dt2.theAnswer, 42)
2235 self.assertEqual(dt2.extra, 7)
2236 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2237 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2238
Armin Rigof4afb212005-11-07 07:15:48 +00002239 def test_backdoor_resistance(self):
2240 # see TestDate.test_backdoor_resistance().
2241 base = '2:59.0'
2242 for hour_byte in ' ', '9', chr(24), '\xff':
2243 self.assertRaises(TypeError, self.theclass,
2244 hour_byte + base[1:])
2245
Tim Peters855fe882002-12-22 03:43:39 +00002246# A mixin for classes with a tzinfo= argument. Subclasses must define
2247# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002248# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002249class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002250
Tim Petersbad8ff02002-12-30 20:52:32 +00002251 def test_argument_passing(self):
2252 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002253 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002254 class introspective(tzinfo):
2255 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002256 def utcoffset(self, dt):
2257 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002258 dst = utcoffset
2259
2260 obj = cls(1, 2, 3, tzinfo=introspective())
2261
Tim Peters0bf60bd2003-01-08 20:40:01 +00002262 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002263 self.assertEqual(obj.tzname(), expected)
2264
Tim Peters0bf60bd2003-01-08 20:40:01 +00002265 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002266 self.assertEqual(obj.utcoffset(), expected)
2267 self.assertEqual(obj.dst(), expected)
2268
Tim Peters855fe882002-12-22 03:43:39 +00002269 def test_bad_tzinfo_classes(self):
2270 cls = self.theclass
2271 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002272
Tim Peters855fe882002-12-22 03:43:39 +00002273 class NiceTry(object):
2274 def __init__(self): pass
2275 def utcoffset(self, dt): pass
2276 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2277
2278 class BetterTry(tzinfo):
2279 def __init__(self): pass
2280 def utcoffset(self, dt): pass
2281 b = BetterTry()
2282 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002283 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002284
2285 def test_utc_offset_out_of_bounds(self):
2286 class Edgy(tzinfo):
2287 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002288 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002289 def utcoffset(self, dt):
2290 return self.offset
2291
2292 cls = self.theclass
2293 for offset, legit in ((-1440, False),
2294 (-1439, True),
2295 (1439, True),
2296 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002297 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002298 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002299 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002300 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002301 else:
2302 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002303 if legit:
2304 aofs = abs(offset)
2305 h, m = divmod(aofs, 60)
2306 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002307 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002308 t = t.timetz()
2309 self.assertEqual(str(t), "01:02:03" + tag)
2310 else:
2311 self.assertRaises(ValueError, str, t)
2312
2313 def test_tzinfo_classes(self):
2314 cls = self.theclass
2315 class C1(tzinfo):
2316 def utcoffset(self, dt): return None
2317 def dst(self, dt): return None
2318 def tzname(self, dt): return None
2319 for t in (cls(1, 1, 1),
2320 cls(1, 1, 1, tzinfo=None),
2321 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002322 self.assertTrue(t.utcoffset() is None)
2323 self.assertTrue(t.dst() is None)
2324 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002325
Tim Peters855fe882002-12-22 03:43:39 +00002326 class C3(tzinfo):
2327 def utcoffset(self, dt): return timedelta(minutes=-1439)
2328 def dst(self, dt): return timedelta(minutes=1439)
2329 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002330 t = cls(1, 1, 1, tzinfo=C3())
2331 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2332 self.assertEqual(t.dst(), timedelta(minutes=1439))
2333 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002334
2335 # Wrong types.
2336 class C4(tzinfo):
2337 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002338 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002339 def tzname(self, dt): return 0
2340 t = cls(1, 1, 1, tzinfo=C4())
2341 self.assertRaises(TypeError, t.utcoffset)
2342 self.assertRaises(TypeError, t.dst)
2343 self.assertRaises(TypeError, t.tzname)
2344
2345 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002346 class C6(tzinfo):
2347 def utcoffset(self, dt): return timedelta(hours=-24)
2348 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002349 t = cls(1, 1, 1, tzinfo=C6())
2350 self.assertRaises(ValueError, t.utcoffset)
2351 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002352
2353 # Not a whole number of minutes.
2354 class C7(tzinfo):
2355 def utcoffset(self, dt): return timedelta(seconds=61)
2356 def dst(self, dt): return timedelta(microseconds=-81)
2357 t = cls(1, 1, 1, tzinfo=C7())
2358 self.assertRaises(ValueError, t.utcoffset)
2359 self.assertRaises(ValueError, t.dst)
2360
Tim Peters4c0db782002-12-26 05:01:19 +00002361 def test_aware_compare(self):
2362 cls = self.theclass
2363
Tim Peters60c76e42002-12-27 00:41:11 +00002364 # Ensure that utcoffset() gets ignored if the comparands have
2365 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002366 class OperandDependentOffset(tzinfo):
2367 def utcoffset(self, t):
2368 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002369 # d0 and d1 equal after adjustment
2370 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002371 else:
Tim Peters397301e2003-01-02 21:28:08 +00002372 # d2 off in the weeds
2373 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002374
2375 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2376 d0 = base.replace(minute=3)
2377 d1 = base.replace(minute=9)
2378 d2 = base.replace(minute=11)
2379 for x in d0, d1, d2:
2380 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002381 for op in lt, le, gt, ge, eq, ne:
2382 got = op(x, y)
2383 expected = op(x.minute, y.minute)
2384 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002385
2386 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002387 # Note that a time can't actually have an operand-depedent offset,
2388 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2389 # so skip this test for time.
2390 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002391 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2392 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2393 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2394 for x in d0, d1, d2:
2395 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002396 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002397 if (x is d0 or x is d1) and (y is d0 or y is d1):
2398 expected = 0
2399 elif x is y is d2:
2400 expected = 0
2401 elif x is d2:
2402 expected = -1
2403 else:
2404 assert y is d2
2405 expected = 1
2406 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002407
Tim Peters855fe882002-12-22 03:43:39 +00002408
Tim Peters0bf60bd2003-01-08 20:40:01 +00002409# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002410class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002411 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002412
2413 def test_empty(self):
2414 t = self.theclass()
2415 self.assertEqual(t.hour, 0)
2416 self.assertEqual(t.minute, 0)
2417 self.assertEqual(t.second, 0)
2418 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002419 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002420
Tim Peters2a799bf2002-12-16 20:18:38 +00002421 def test_zones(self):
2422 est = FixedOffset(-300, "EST", 1)
2423 utc = FixedOffset(0, "UTC", -2)
2424 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002425 t1 = time( 7, 47, tzinfo=est)
2426 t2 = time(12, 47, tzinfo=utc)
2427 t3 = time(13, 47, tzinfo=met)
2428 t4 = time(microsecond=40)
2429 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002430
2431 self.assertEqual(t1.tzinfo, est)
2432 self.assertEqual(t2.tzinfo, utc)
2433 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002434 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002435 self.assertEqual(t5.tzinfo, utc)
2436
Tim Peters855fe882002-12-22 03:43:39 +00002437 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2438 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2439 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002440 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002441 self.assertRaises(TypeError, t1.utcoffset, "no args")
2442
2443 self.assertEqual(t1.tzname(), "EST")
2444 self.assertEqual(t2.tzname(), "UTC")
2445 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002446 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002447 self.assertRaises(TypeError, t1.tzname, "no args")
2448
Tim Peters855fe882002-12-22 03:43:39 +00002449 self.assertEqual(t1.dst(), timedelta(minutes=1))
2450 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2451 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002452 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002453 self.assertRaises(TypeError, t1.dst, "no args")
2454
2455 self.assertEqual(hash(t1), hash(t2))
2456 self.assertEqual(hash(t1), hash(t3))
2457 self.assertEqual(hash(t2), hash(t3))
2458
2459 self.assertEqual(t1, t2)
2460 self.assertEqual(t1, t3)
2461 self.assertEqual(t2, t3)
2462 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2463 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2464 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2465
2466 self.assertEqual(str(t1), "07:47:00-05:00")
2467 self.assertEqual(str(t2), "12:47:00+00:00")
2468 self.assertEqual(str(t3), "13:47:00+01:00")
2469 self.assertEqual(str(t4), "00:00:00.000040")
2470 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2471
2472 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2473 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2474 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2475 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2476 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2477
Tim Peters0bf60bd2003-01-08 20:40:01 +00002478 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002479 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2480 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2481 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2482 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2483 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2484
2485 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2486 "07:47:00 %Z=EST %z=-0500")
2487 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2488 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2489
2490 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002491 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002492 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2493 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2494
Tim Petersb92bb712002-12-21 17:44:07 +00002495 # Check that an invalid tzname result raises an exception.
2496 class Badtzname(tzinfo):
2497 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002498 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002499 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2500 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002501
2502 def test_hash_edge_cases(self):
2503 # Offsets that overflow a basic time.
2504 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2505 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2506 self.assertEqual(hash(t1), hash(t2))
2507
2508 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2509 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2510 self.assertEqual(hash(t1), hash(t2))
2511
Tim Peters2a799bf2002-12-16 20:18:38 +00002512 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002513 # Try one without a tzinfo.
2514 args = 20, 59, 16, 64**2
2515 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002516 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002517 green = pickler.dumps(orig, proto)
2518 derived = unpickler.loads(green)
2519 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002520
2521 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002522 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002523 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002524 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002525 green = pickler.dumps(orig, proto)
2526 derived = unpickler.loads(green)
2527 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002528 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002529 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2530 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002531
2532 def test_more_bool(self):
2533 # Test cases with non-None tzinfo.
2534 cls = self.theclass
2535
2536 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002537 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002538
2539 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002540 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002541
2542 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002543 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002544
2545 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002546 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002547
2548 # Mostly ensuring this doesn't overflow internally.
2549 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002550 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002551
2552 # But this should yield a value error -- the utcoffset is bogus.
2553 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2554 self.assertRaises(ValueError, lambda: bool(t))
2555
2556 # Likewise.
2557 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2558 self.assertRaises(ValueError, lambda: bool(t))
2559
Tim Peters12bf3392002-12-24 05:41:27 +00002560 def test_replace(self):
2561 cls = self.theclass
2562 z100 = FixedOffset(100, "+100")
2563 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2564 args = [1, 2, 3, 4, z100]
2565 base = cls(*args)
2566 self.assertEqual(base, base.replace())
2567
2568 i = 0
2569 for name, newval in (("hour", 5),
2570 ("minute", 6),
2571 ("second", 7),
2572 ("microsecond", 8),
2573 ("tzinfo", zm200)):
2574 newargs = args[:]
2575 newargs[i] = newval
2576 expected = cls(*newargs)
2577 got = base.replace(**{name: newval})
2578 self.assertEqual(expected, got)
2579 i += 1
2580
2581 # Ensure we can get rid of a tzinfo.
2582 self.assertEqual(base.tzname(), "+100")
2583 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002584 self.assertTrue(base2.tzinfo is None)
2585 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002586
2587 # Ensure we can add one.
2588 base3 = base2.replace(tzinfo=z100)
2589 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002590 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002591
2592 # Out of bounds.
2593 base = cls(1)
2594 self.assertRaises(ValueError, base.replace, hour=24)
2595 self.assertRaises(ValueError, base.replace, minute=-1)
2596 self.assertRaises(ValueError, base.replace, second=100)
2597 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2598
Tim Peters60c76e42002-12-27 00:41:11 +00002599 def test_mixed_compare(self):
2600 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002601 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002602 self.assertEqual(t1, t2)
2603 t2 = t2.replace(tzinfo=None)
2604 self.assertEqual(t1, t2)
2605 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2606 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002607 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2608 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002609
Tim Peters0bf60bd2003-01-08 20:40:01 +00002610 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002611 class Varies(tzinfo):
2612 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002613 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002614 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002615 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002616 return self.offset
2617
2618 v = Varies()
2619 t1 = t2.replace(tzinfo=v)
2620 t2 = t2.replace(tzinfo=v)
2621 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2622 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2623 self.assertEqual(t1, t2)
2624
2625 # But if they're not identical, it isn't ignored.
2626 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002627 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002628
Tim Petersa98924a2003-05-17 05:55:19 +00002629 def test_subclass_timetz(self):
2630
2631 class C(self.theclass):
2632 theAnswer = 42
2633
2634 def __new__(cls, *args, **kws):
2635 temp = kws.copy()
2636 extra = temp.pop('extra')
2637 result = self.theclass.__new__(cls, *args, **temp)
2638 result.extra = extra
2639 return result
2640
2641 def newmeth(self, start):
2642 return start + self.hour + self.second
2643
2644 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2645
2646 dt1 = self.theclass(*args)
2647 dt2 = C(*args, **{'extra': 7})
2648
2649 self.assertEqual(dt2.__class__, C)
2650 self.assertEqual(dt2.theAnswer, 42)
2651 self.assertEqual(dt2.extra, 7)
2652 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2653 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2654
Tim Peters4c0db782002-12-26 05:01:19 +00002655
Tim Peters0bf60bd2003-01-08 20:40:01 +00002656# Testing datetime objects with a non-None tzinfo.
2657
Guido van Rossumd8faa362007-04-27 19:54:29 +00002658class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002659 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002660
2661 def test_trivial(self):
2662 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2663 self.assertEqual(dt.year, 1)
2664 self.assertEqual(dt.month, 2)
2665 self.assertEqual(dt.day, 3)
2666 self.assertEqual(dt.hour, 4)
2667 self.assertEqual(dt.minute, 5)
2668 self.assertEqual(dt.second, 6)
2669 self.assertEqual(dt.microsecond, 7)
2670 self.assertEqual(dt.tzinfo, None)
2671
2672 def test_even_more_compare(self):
2673 # The test_compare() and test_more_compare() inherited from TestDate
2674 # and TestDateTime covered non-tzinfo cases.
2675
2676 # Smallest possible after UTC adjustment.
2677 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2678 # Largest possible after UTC adjustment.
2679 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2680 tzinfo=FixedOffset(-1439, ""))
2681
2682 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002683 self.assertTrue(t1 < t2)
2684 self.assertTrue(t1 != t2)
2685 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002686
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002687 self.assertEqual(t1, t1)
2688 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002689
2690 # Equal afer adjustment.
2691 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2692 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2693 self.assertEqual(t1, t2)
2694
2695 # Change t1 not to subtract a minute, and t1 should be larger.
2696 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002697 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002698
2699 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2700 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002701 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002702
2703 # Back to the original t1, but make seconds resolve it.
2704 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2705 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002706 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002707
2708 # Likewise, but make microseconds resolve it.
2709 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2710 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002711 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002712
2713 # Make t2 naive and it should fail.
2714 t2 = self.theclass.min
2715 self.assertRaises(TypeError, lambda: t1 == t2)
2716 self.assertEqual(t2, t2)
2717
2718 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2719 class Naive(tzinfo):
2720 def utcoffset(self, dt): return None
2721 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2722 self.assertRaises(TypeError, lambda: t1 == t2)
2723 self.assertEqual(t2, t2)
2724
2725 # OTOH, it's OK to compare two of these mixing the two ways of being
2726 # naive.
2727 t1 = self.theclass(5, 6, 7)
2728 self.assertEqual(t1, t2)
2729
2730 # Try a bogus uctoffset.
2731 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002732 def utcoffset(self, dt):
2733 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002734 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2735 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002736 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002737
Tim Peters2a799bf2002-12-16 20:18:38 +00002738 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002739 # Try one without a tzinfo.
2740 args = 6, 7, 23, 20, 59, 1, 64**2
2741 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002742 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002743 green = pickler.dumps(orig, proto)
2744 derived = unpickler.loads(green)
2745 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002746
2747 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002748 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002750 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002751 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002752 green = pickler.dumps(orig, proto)
2753 derived = unpickler.loads(green)
2754 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002755 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002756 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2757 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002758
2759 def test_extreme_hashes(self):
2760 # If an attempt is made to hash these via subtracting the offset
2761 # then hashing a datetime object, OverflowError results. The
2762 # Python implementation used to blow up here.
2763 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2764 hash(t)
2765 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2766 tzinfo=FixedOffset(-1439, ""))
2767 hash(t)
2768
2769 # OTOH, an OOB offset should blow up.
2770 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2771 self.assertRaises(ValueError, hash, t)
2772
2773 def test_zones(self):
2774 est = FixedOffset(-300, "EST")
2775 utc = FixedOffset(0, "UTC")
2776 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002777 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2778 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2779 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002780 self.assertEqual(t1.tzinfo, est)
2781 self.assertEqual(t2.tzinfo, utc)
2782 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002783 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2784 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2785 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002786 self.assertEqual(t1.tzname(), "EST")
2787 self.assertEqual(t2.tzname(), "UTC")
2788 self.assertEqual(t3.tzname(), "MET")
2789 self.assertEqual(hash(t1), hash(t2))
2790 self.assertEqual(hash(t1), hash(t3))
2791 self.assertEqual(hash(t2), hash(t3))
2792 self.assertEqual(t1, t2)
2793 self.assertEqual(t1, t3)
2794 self.assertEqual(t2, t3)
2795 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2796 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2797 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002798 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002799 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2800 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2801 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2802
2803 def test_combine(self):
2804 met = FixedOffset(60, "MET")
2805 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002806 tz = time(18, 45, 3, 1234, tzinfo=met)
2807 dt = datetime.combine(d, tz)
2808 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002809 tzinfo=met))
2810
2811 def test_extract(self):
2812 met = FixedOffset(60, "MET")
2813 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2814 self.assertEqual(dt.date(), date(2002, 3, 4))
2815 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002816 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002817
2818 def test_tz_aware_arithmetic(self):
2819 import random
2820
2821 now = self.theclass.now()
2822 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002823 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002824 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002825 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002826 self.assertEqual(nowaware.timetz(), timeaware)
2827
2828 # Can't mix aware and non-aware.
2829 self.assertRaises(TypeError, lambda: now - nowaware)
2830 self.assertRaises(TypeError, lambda: nowaware - now)
2831
Tim Peters0bf60bd2003-01-08 20:40:01 +00002832 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002833 self.assertRaises(TypeError, lambda: now + nowaware)
2834 self.assertRaises(TypeError, lambda: nowaware + now)
2835 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2836
2837 # Subtracting should yield 0.
2838 self.assertEqual(now - now, timedelta(0))
2839 self.assertEqual(nowaware - nowaware, timedelta(0))
2840
2841 # Adding a delta should preserve tzinfo.
2842 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2843 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002844 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002845 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002846 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002847 self.assertEqual(nowawareplus, nowawareplus2)
2848
2849 # that - delta should be what we started with, and that - what we
2850 # started with should be delta.
2851 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002852 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002853 self.assertEqual(nowaware, diff)
2854 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2855 self.assertEqual(nowawareplus - nowaware, delta)
2856
2857 # Make up a random timezone.
2858 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002859 # Attach it to nowawareplus.
2860 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002861 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002862 # Make sure the difference takes the timezone adjustments into account.
2863 got = nowaware - nowawareplus
2864 # Expected: (nowaware base - nowaware offset) -
2865 # (nowawareplus base - nowawareplus offset) =
2866 # (nowaware base - nowawareplus base) +
2867 # (nowawareplus offset - nowaware offset) =
2868 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002869 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002870 self.assertEqual(got, expected)
2871
2872 # Try max possible difference.
2873 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2874 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2875 tzinfo=FixedOffset(-1439, "max"))
2876 maxdiff = max - min
2877 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2878 timedelta(minutes=2*1439))
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00002879 # Different tzinfo, but the same offset
2880 tza = timezone(HOUR, 'A')
2881 tzb = timezone(HOUR, 'B')
2882 delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
2883 self.assertEqual(delta, self.theclass.min - self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00002884
2885 def test_tzinfo_now(self):
2886 meth = self.theclass.now
2887 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2888 base = meth()
2889 # Try with and without naming the keyword.
2890 off42 = FixedOffset(42, "42")
2891 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002892 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002893 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002894 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002895 # Bad argument with and w/o naming the keyword.
2896 self.assertRaises(TypeError, meth, 16)
2897 self.assertRaises(TypeError, meth, tzinfo=16)
2898 # Bad keyword name.
2899 self.assertRaises(TypeError, meth, tinfo=off42)
2900 # Too many args.
2901 self.assertRaises(TypeError, meth, off42, off42)
2902
Tim Peters10cadce2003-01-23 19:58:02 +00002903 # We don't know which time zone we're in, and don't have a tzinfo
2904 # class to represent it, so seeing whether a tz argument actually
2905 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002906 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002907 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2908 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2909 for dummy in range(3):
2910 now = datetime.now(weirdtz)
2911 self.assertTrue(now.tzinfo is weirdtz)
2912 utcnow = datetime.utcnow().replace(tzinfo=utc)
2913 now2 = utcnow.astimezone(weirdtz)
2914 if abs(now - now2) < timedelta(seconds=30):
2915 break
2916 # Else the code is broken, or more than 30 seconds passed between
2917 # calls; assuming the latter, just try again.
2918 else:
2919 # Three strikes and we're out.
2920 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002921
Tim Peters2a799bf2002-12-16 20:18:38 +00002922 def test_tzinfo_fromtimestamp(self):
2923 import time
2924 meth = self.theclass.fromtimestamp
2925 ts = time.time()
2926 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2927 base = meth(ts)
2928 # Try with and without naming the keyword.
2929 off42 = FixedOffset(42, "42")
2930 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002931 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002932 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002933 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002934 # Bad argument with and w/o naming the keyword.
2935 self.assertRaises(TypeError, meth, ts, 16)
2936 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2937 # Bad keyword name.
2938 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2939 # Too many args.
2940 self.assertRaises(TypeError, meth, ts, off42, off42)
2941 # Too few args.
2942 self.assertRaises(TypeError, meth)
2943
Tim Peters2a44a8d2003-01-23 20:53:10 +00002944 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002945 timestamp = 1000000000
2946 utcdatetime = datetime.utcfromtimestamp(timestamp)
2947 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2948 # But on some flavor of Mac, it's nowhere near that. So we can't have
2949 # any idea here what time that actually is, we can only test that
2950 # relative changes match.
2951 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2952 tz = FixedOffset(utcoffset, "tz", 0)
2953 expected = utcdatetime + utcoffset
2954 got = datetime.fromtimestamp(timestamp, tz)
2955 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002956
Tim Peters2a799bf2002-12-16 20:18:38 +00002957 def test_tzinfo_utcnow(self):
2958 meth = self.theclass.utcnow
2959 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2960 base = meth()
2961 # Try with and without naming the keyword; for whatever reason,
2962 # utcnow() doesn't accept a tzinfo argument.
2963 off42 = FixedOffset(42, "42")
2964 self.assertRaises(TypeError, meth, off42)
2965 self.assertRaises(TypeError, meth, tzinfo=off42)
2966
2967 def test_tzinfo_utcfromtimestamp(self):
2968 import time
2969 meth = self.theclass.utcfromtimestamp
2970 ts = time.time()
2971 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2972 base = meth(ts)
2973 # Try with and without naming the keyword; for whatever reason,
2974 # utcfromtimestamp() doesn't accept a tzinfo argument.
2975 off42 = FixedOffset(42, "42")
2976 self.assertRaises(TypeError, meth, ts, off42)
2977 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2978
2979 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002980 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002981 # DST flag.
2982 class DST(tzinfo):
2983 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002984 if isinstance(dstvalue, int):
2985 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002986 self.dstvalue = dstvalue
2987 def dst(self, dt):
2988 return self.dstvalue
2989
2990 cls = self.theclass
2991 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2992 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2993 t = d.timetuple()
2994 self.assertEqual(1, t.tm_year)
2995 self.assertEqual(1, t.tm_mon)
2996 self.assertEqual(1, t.tm_mday)
2997 self.assertEqual(10, t.tm_hour)
2998 self.assertEqual(20, t.tm_min)
2999 self.assertEqual(30, t.tm_sec)
3000 self.assertEqual(0, t.tm_wday)
3001 self.assertEqual(1, t.tm_yday)
3002 self.assertEqual(flag, t.tm_isdst)
3003
3004 # dst() returns wrong type.
3005 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
3006
3007 # dst() at the edge.
3008 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
3009 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
3010
3011 # dst() out of range.
3012 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
3013 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
3014
3015 def test_utctimetuple(self):
3016 class DST(tzinfo):
Alexander Belopolskyf34e82e2010-07-05 15:05:33 +00003017 def __init__(self, dstvalue=0):
Tim Peters397301e2003-01-02 21:28:08 +00003018 if isinstance(dstvalue, int):
3019 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00003020 self.dstvalue = dstvalue
3021 def dst(self, dt):
3022 return self.dstvalue
3023
3024 cls = self.theclass
3025 # This can't work: DST didn't implement utcoffset.
3026 self.assertRaises(NotImplementedError,
3027 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3028
3029 class UOFS(DST):
3030 def __init__(self, uofs, dofs=None):
3031 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00003032 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00003033 def utcoffset(self, dt):
3034 return self.uofs
3035
Tim Peters2a799bf2002-12-16 20:18:38 +00003036 for dstvalue in -33, 33, 0, None:
3037 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3038 t = d.utctimetuple()
3039 self.assertEqual(d.year, t.tm_year)
3040 self.assertEqual(d.month, t.tm_mon)
3041 self.assertEqual(d.day, t.tm_mday)
3042 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3043 self.assertEqual(13, t.tm_min)
3044 self.assertEqual(d.second, t.tm_sec)
3045 self.assertEqual(d.weekday(), t.tm_wday)
3046 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3047 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003048 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3049 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003050 self.assertEqual(0, t.tm_isdst)
3051
Alexander Belopolskyf34e82e2010-07-05 15:05:33 +00003052 # For naive datetime, utctimetuple == timetuple except for isdst
3053 d = cls(1, 2, 3, 10, 20, 30, 40)
3054 t = d.utctimetuple()
3055 self.assertEqual(t[:-1], d.timetuple()[:-1])
3056 self.assertEqual(0, t.tm_isdst)
3057 # Same if utcoffset is None
3058 class NOFS(DST):
3059 def utcoffset(self, dt):
3060 return None
3061 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
3062 t = d.utctimetuple()
3063 self.assertEqual(t[:-1], d.timetuple()[:-1])
3064 self.assertEqual(0, t.tm_isdst)
3065 # Check that bad tzinfo is detected
3066 class BOFS(DST):
3067 def utcoffset(self, dt):
3068 return "EST"
3069 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
3070 self.assertRaises(TypeError, d.utctimetuple)
3071
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003072 # Check that utctimetuple() is the same as
3073 # astimezone(utc).timetuple()
3074 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3075 for tz in [timezone.min, timezone.utc, timezone.max]:
3076 dtz = d.replace(tzinfo=tz)
3077 self.assertEqual(dtz.utctimetuple()[:-1],
3078 dtz.astimezone(timezone.utc).timetuple()[:-1])
3079 # At the edges, UTC adjustment can produce years out-of-range
3080 # for a datetime object. Ensure that an OverflowError is
3081 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003082 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3083 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003084 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003085
3086 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3087 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003088 self.assertRaises(OverflowError, huge.utctimetuple)
3089 # More overflow cases
3090 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3091 self.assertRaises(OverflowError, tiny.utctimetuple)
3092 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3093 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003094
3095 def test_tzinfo_isoformat(self):
3096 zero = FixedOffset(0, "+00:00")
3097 plus = FixedOffset(220, "+03:40")
3098 minus = FixedOffset(-231, "-03:51")
3099 unknown = FixedOffset(None, "")
3100
3101 cls = self.theclass
3102 datestr = '0001-02-03'
3103 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003104 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003105 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3106 timestr = '04:05:59' + (us and '.987001' or '')
3107 ofsstr = ofs is not None and d.tzname() or ''
3108 tailstr = timestr + ofsstr
3109 iso = d.isoformat()
3110 self.assertEqual(iso, datestr + 'T' + tailstr)
3111 self.assertEqual(iso, d.isoformat('T'))
3112 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003113 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003114 self.assertEqual(str(d), datestr + ' ' + tailstr)
3115
Tim Peters12bf3392002-12-24 05:41:27 +00003116 def test_replace(self):
3117 cls = self.theclass
3118 z100 = FixedOffset(100, "+100")
3119 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3120 args = [1, 2, 3, 4, 5, 6, 7, z100]
3121 base = cls(*args)
3122 self.assertEqual(base, base.replace())
3123
3124 i = 0
3125 for name, newval in (("year", 2),
3126 ("month", 3),
3127 ("day", 4),
3128 ("hour", 5),
3129 ("minute", 6),
3130 ("second", 7),
3131 ("microsecond", 8),
3132 ("tzinfo", zm200)):
3133 newargs = args[:]
3134 newargs[i] = newval
3135 expected = cls(*newargs)
3136 got = base.replace(**{name: newval})
3137 self.assertEqual(expected, got)
3138 i += 1
3139
3140 # Ensure we can get rid of a tzinfo.
3141 self.assertEqual(base.tzname(), "+100")
3142 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003143 self.assertTrue(base2.tzinfo is None)
3144 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003145
3146 # Ensure we can add one.
3147 base3 = base2.replace(tzinfo=z100)
3148 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003149 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003150
3151 # Out of bounds.
3152 base = cls(2000, 2, 29)
3153 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003154
Tim Peters80475bb2002-12-25 07:40:55 +00003155 def test_more_astimezone(self):
3156 # The inherited test_astimezone covered some trivial and error cases.
3157 fnone = FixedOffset(None, "None")
3158 f44m = FixedOffset(44, "44")
3159 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3160
Tim Peters10cadce2003-01-23 19:58:02 +00003161 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003162 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003163 # Replacing with degenerate tzinfo raises an exception.
3164 self.assertRaises(ValueError, dt.astimezone, fnone)
3165 # Ditto with None tz.
3166 self.assertRaises(TypeError, dt.astimezone, None)
3167 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003168 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003169 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003170 self.assertEqual(x.date(), dt.date())
3171 self.assertEqual(x.time(), dt.time())
3172
3173 # Replacing with different tzinfo does adjust.
3174 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003175 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003176 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3177 expected = dt - dt.utcoffset() # in effect, convert to UTC
3178 expected += fm5h.utcoffset(dt) # and from there to local time
3179 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3180 self.assertEqual(got.date(), expected.date())
3181 self.assertEqual(got.time(), expected.time())
3182 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003183 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003184 self.assertEqual(got, expected)
3185
Tim Peters4c0db782002-12-26 05:01:19 +00003186 def test_aware_subtract(self):
3187 cls = self.theclass
3188
Tim Peters60c76e42002-12-27 00:41:11 +00003189 # Ensure that utcoffset() is ignored when the operands have the
3190 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003191 class OperandDependentOffset(tzinfo):
3192 def utcoffset(self, t):
3193 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003194 # d0 and d1 equal after adjustment
3195 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003196 else:
Tim Peters397301e2003-01-02 21:28:08 +00003197 # d2 off in the weeds
3198 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003199
3200 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3201 d0 = base.replace(minute=3)
3202 d1 = base.replace(minute=9)
3203 d2 = base.replace(minute=11)
3204 for x in d0, d1, d2:
3205 for y in d0, d1, d2:
3206 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003207 expected = timedelta(minutes=x.minute - y.minute)
3208 self.assertEqual(got, expected)
3209
3210 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3211 # ignored.
3212 base = cls(8, 9, 10, 11, 12, 13, 14)
3213 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3214 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3215 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3216 for x in d0, d1, d2:
3217 for y in d0, d1, d2:
3218 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003219 if (x is d0 or x is d1) and (y is d0 or y is d1):
3220 expected = timedelta(0)
3221 elif x is y is d2:
3222 expected = timedelta(0)
3223 elif x is d2:
3224 expected = timedelta(minutes=(11-59)-0)
3225 else:
3226 assert y is d2
3227 expected = timedelta(minutes=0-(11-59))
3228 self.assertEqual(got, expected)
3229
Tim Peters60c76e42002-12-27 00:41:11 +00003230 def test_mixed_compare(self):
3231 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003232 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003233 self.assertEqual(t1, t2)
3234 t2 = t2.replace(tzinfo=None)
3235 self.assertEqual(t1, t2)
3236 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3237 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003238 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3239 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003240
Tim Peters0bf60bd2003-01-08 20:40:01 +00003241 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003242 class Varies(tzinfo):
3243 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003244 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003245 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003246 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003247 return self.offset
3248
3249 v = Varies()
3250 t1 = t2.replace(tzinfo=v)
3251 t2 = t2.replace(tzinfo=v)
3252 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3253 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3254 self.assertEqual(t1, t2)
3255
3256 # But if they're not identical, it isn't ignored.
3257 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003258 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003259
Tim Petersa98924a2003-05-17 05:55:19 +00003260 def test_subclass_datetimetz(self):
3261
3262 class C(self.theclass):
3263 theAnswer = 42
3264
3265 def __new__(cls, *args, **kws):
3266 temp = kws.copy()
3267 extra = temp.pop('extra')
3268 result = self.theclass.__new__(cls, *args, **temp)
3269 result.extra = extra
3270 return result
3271
3272 def newmeth(self, start):
3273 return start + self.hour + self.year
3274
3275 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3276
3277 dt1 = self.theclass(*args)
3278 dt2 = C(*args, **{'extra': 7})
3279
3280 self.assertEqual(dt2.__class__, C)
3281 self.assertEqual(dt2.theAnswer, 42)
3282 self.assertEqual(dt2.extra, 7)
3283 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3284 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3285
Tim Peters621818b2002-12-29 23:44:49 +00003286# Pain to set up DST-aware tzinfo classes.
3287
3288def first_sunday_on_or_after(dt):
3289 days_to_go = 6 - dt.weekday()
3290 if days_to_go:
3291 dt += timedelta(days_to_go)
3292 return dt
3293
3294ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003295MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003296HOUR = timedelta(hours=1)
3297DAY = timedelta(days=1)
3298# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3299DSTSTART = datetime(1, 4, 1, 2)
3300# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003301# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3302# being standard time on that day, there is no spelling in local time of
3303# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3304DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003305
3306class USTimeZone(tzinfo):
3307
3308 def __init__(self, hours, reprname, stdname, dstname):
3309 self.stdoffset = timedelta(hours=hours)
3310 self.reprname = reprname
3311 self.stdname = stdname
3312 self.dstname = dstname
3313
3314 def __repr__(self):
3315 return self.reprname
3316
3317 def tzname(self, dt):
3318 if self.dst(dt):
3319 return self.dstname
3320 else:
3321 return self.stdname
3322
3323 def utcoffset(self, dt):
3324 return self.stdoffset + self.dst(dt)
3325
3326 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003327 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003328 # An exception instead may be sensible here, in one or more of
3329 # the cases.
3330 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003331 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003332
3333 # Find first Sunday in April.
3334 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3335 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3336
3337 # Find last Sunday in October.
3338 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3339 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3340
Tim Peters621818b2002-12-29 23:44:49 +00003341 # Can't compare naive to aware objects, so strip the timezone from
3342 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003343 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003344 return HOUR
3345 else:
3346 return ZERO
3347
Tim Peters521fc152002-12-31 17:36:56 +00003348Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3349Central = USTimeZone(-6, "Central", "CST", "CDT")
3350Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3351Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003352utc_real = FixedOffset(0, "UTC", 0)
3353# For better test coverage, we want another flavor of UTC that's west of
3354# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003355utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003356
3357class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003358 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003359 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003360 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003361
Tim Peters0bf60bd2003-01-08 20:40:01 +00003362 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003363
Tim Peters521fc152002-12-31 17:36:56 +00003364 # Check a time that's inside DST.
3365 def checkinside(self, dt, tz, utc, dston, dstoff):
3366 self.assertEqual(dt.dst(), HOUR)
3367
3368 # Conversion to our own timezone is always an identity.
3369 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003370
3371 asutc = dt.astimezone(utc)
3372 there_and_back = asutc.astimezone(tz)
3373
3374 # Conversion to UTC and back isn't always an identity here,
3375 # because there are redundant spellings (in local time) of
3376 # UTC time when DST begins: the clock jumps from 1:59:59
3377 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3378 # make sense then. The classes above treat 2:MM:SS as
3379 # daylight time then (it's "after 2am"), really an alias
3380 # for 1:MM:SS standard time. The latter form is what
3381 # conversion back from UTC produces.
3382 if dt.date() == dston.date() and dt.hour == 2:
3383 # We're in the redundant hour, and coming back from
3384 # UTC gives the 1:MM:SS standard-time spelling.
3385 self.assertEqual(there_and_back + HOUR, dt)
3386 # Although during was considered to be in daylight
3387 # time, there_and_back is not.
3388 self.assertEqual(there_and_back.dst(), ZERO)
3389 # They're the same times in UTC.
3390 self.assertEqual(there_and_back.astimezone(utc),
3391 dt.astimezone(utc))
3392 else:
3393 # We're not in the redundant hour.
3394 self.assertEqual(dt, there_and_back)
3395
Tim Peters327098a2003-01-20 22:54:38 +00003396 # Because we have a redundant spelling when DST begins, there is
3397 # (unforunately) an hour when DST ends that can't be spelled at all in
3398 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3399 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3400 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3401 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3402 # expressed in local time. Nevertheless, we want conversion back
3403 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003404 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003405 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003406 if dt.date() == dstoff.date() and dt.hour == 0:
3407 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003408 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003409 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3410 nexthour_utc += HOUR
3411 nexthour_tz = nexthour_utc.astimezone(tz)
3412 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003413 else:
Tim Peters327098a2003-01-20 22:54:38 +00003414 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003415
3416 # Check a time that's outside DST.
3417 def checkoutside(self, dt, tz, utc):
3418 self.assertEqual(dt.dst(), ZERO)
3419
3420 # Conversion to our own timezone is always an identity.
3421 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003422
3423 # Converting to UTC and back is an identity too.
3424 asutc = dt.astimezone(utc)
3425 there_and_back = asutc.astimezone(tz)
3426 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003427
Tim Peters1024bf82002-12-30 17:09:40 +00003428 def convert_between_tz_and_utc(self, tz, utc):
3429 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003430 # Because 1:MM on the day DST ends is taken as being standard time,
3431 # there is no spelling in tz for the last hour of daylight time.
3432 # For purposes of the test, the last hour of DST is 0:MM, which is
3433 # taken as being daylight time (and 1:MM is taken as being standard
3434 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003435 dstoff = self.dstoff.replace(tzinfo=tz)
3436 for delta in (timedelta(weeks=13),
3437 DAY,
3438 HOUR,
3439 timedelta(minutes=1),
3440 timedelta(microseconds=1)):
3441
Tim Peters521fc152002-12-31 17:36:56 +00003442 self.checkinside(dston, tz, utc, dston, dstoff)
3443 for during in dston + delta, dstoff - delta:
3444 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003445
Tim Peters521fc152002-12-31 17:36:56 +00003446 self.checkoutside(dstoff, tz, utc)
3447 for outside in dston - delta, dstoff + delta:
3448 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003449
Tim Peters621818b2002-12-29 23:44:49 +00003450 def test_easy(self):
3451 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003452 self.convert_between_tz_and_utc(Eastern, utc_real)
3453 self.convert_between_tz_and_utc(Pacific, utc_real)
3454 self.convert_between_tz_and_utc(Eastern, utc_fake)
3455 self.convert_between_tz_and_utc(Pacific, utc_fake)
3456 # The next is really dancing near the edge. It works because
3457 # Pacific and Eastern are far enough apart that their "problem
3458 # hours" don't overlap.
3459 self.convert_between_tz_and_utc(Eastern, Pacific)
3460 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003461 # OTOH, these fail! Don't enable them. The difficulty is that
3462 # the edge case tests assume that every hour is representable in
3463 # the "utc" class. This is always true for a fixed-offset tzinfo
3464 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3465 # For these adjacent DST-aware time zones, the range of time offsets
3466 # tested ends up creating hours in the one that aren't representable
3467 # in the other. For the same reason, we would see failures in the
3468 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3469 # offset deltas in convert_between_tz_and_utc().
3470 #
3471 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3472 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003473
Tim Petersf3615152003-01-01 21:51:37 +00003474 def test_tricky(self):
3475 # 22:00 on day before daylight starts.
3476 fourback = self.dston - timedelta(hours=4)
3477 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003478 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003479 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3480 # 2", we should get the 3 spelling.
3481 # If we plug 22:00 the day before into Eastern, it "looks like std
3482 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3483 # to 22:00 lands on 2:00, which makes no sense in local time (the
3484 # local clock jumps from 1 to 3). The point here is to make sure we
3485 # get the 3 spelling.
3486 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003487 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003488 self.assertEqual(expected, got)
3489
3490 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3491 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003492 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003493 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3494 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3495 # spelling.
3496 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003497 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003498 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003499
Tim Petersadf64202003-01-04 06:03:15 +00003500 # Now on the day DST ends, we want "repeat an hour" behavior.
3501 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3502 # EST 23:MM 0:MM 1:MM 2:MM
3503 # EDT 0:MM 1:MM 2:MM 3:MM
3504 # wall 0:MM 1:MM 1:MM 2:MM against these
3505 for utc in utc_real, utc_fake:
3506 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003507 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003508 # Convert that to UTC.
3509 first_std_hour -= tz.utcoffset(None)
3510 # Adjust for possibly fake UTC.
3511 asutc = first_std_hour + utc.utcoffset(None)
3512 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3513 # tz=Eastern.
3514 asutcbase = asutc.replace(tzinfo=utc)
3515 for tzhour in (0, 1, 1, 2):
3516 expectedbase = self.dstoff.replace(hour=tzhour)
3517 for minute in 0, 30, 59:
3518 expected = expectedbase.replace(minute=minute)
3519 asutc = asutcbase.replace(minute=minute)
3520 astz = asutc.astimezone(tz)
3521 self.assertEqual(astz.replace(tzinfo=None), expected)
3522 asutcbase += HOUR
3523
3524
Tim Peters710fb152003-01-02 19:35:54 +00003525 def test_bogus_dst(self):
3526 class ok(tzinfo):
3527 def utcoffset(self, dt): return HOUR
3528 def dst(self, dt): return HOUR
3529
3530 now = self.theclass.now().replace(tzinfo=utc_real)
3531 # Doesn't blow up.
3532 now.astimezone(ok())
3533
3534 # Does blow up.
3535 class notok(ok):
3536 def dst(self, dt): return None
3537 self.assertRaises(ValueError, now.astimezone, notok())
3538
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003539 # Sometimes blow up. In the following, tzinfo.dst()
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00003540 # implementation may return None or not None depending on
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003541 # whether DST is assumed to be in effect. In this situation,
3542 # a ValueError should be raised by astimezone().
3543 class tricky_notok(ok):
3544 def dst(self, dt):
3545 if dt.year == 2000:
3546 return None
3547 else:
3548 return 10*HOUR
3549 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3550 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3551
Tim Peters52dcce22003-01-23 16:36:11 +00003552 def test_fromutc(self):
3553 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3554 now = datetime.utcnow().replace(tzinfo=utc_real)
3555 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3556 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3557 enow = Eastern.fromutc(now) # doesn't blow up
3558 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3559 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3560 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3561
3562 # Always converts UTC to standard time.
3563 class FauxUSTimeZone(USTimeZone):
3564 def fromutc(self, dt):
3565 return dt + self.stdoffset
3566 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3567
3568 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3569 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3570 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3571
3572 # Check around DST start.
3573 start = self.dston.replace(hour=4, tzinfo=Eastern)
3574 fstart = start.replace(tzinfo=FEastern)
3575 for wall in 23, 0, 1, 3, 4, 5:
3576 expected = start.replace(hour=wall)
3577 if wall == 23:
3578 expected -= timedelta(days=1)
3579 got = Eastern.fromutc(start)
3580 self.assertEqual(expected, got)
3581
3582 expected = fstart + FEastern.stdoffset
3583 got = FEastern.fromutc(fstart)
3584 self.assertEqual(expected, got)
3585
3586 # Ensure astimezone() calls fromutc() too.
3587 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3588 self.assertEqual(expected, got)
3589
3590 start += HOUR
3591 fstart += HOUR
3592
3593 # Check around DST end.
3594 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3595 fstart = start.replace(tzinfo=FEastern)
3596 for wall in 0, 1, 1, 2, 3, 4:
3597 expected = start.replace(hour=wall)
3598 got = Eastern.fromutc(start)
3599 self.assertEqual(expected, got)
3600
3601 expected = fstart + FEastern.stdoffset
3602 got = FEastern.fromutc(fstart)
3603 self.assertEqual(expected, got)
3604
3605 # Ensure astimezone() calls fromutc() too.
3606 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3607 self.assertEqual(expected, got)
3608
3609 start += HOUR
3610 fstart += HOUR
3611
Tim Peters710fb152003-01-02 19:35:54 +00003612
Tim Peters528ca532004-09-16 01:30:50 +00003613#############################################################################
3614# oddballs
3615
3616class Oddballs(unittest.TestCase):
3617
3618 def test_bug_1028306(self):
3619 # Trying to compare a date to a datetime should act like a mixed-
3620 # type comparison, despite that datetime is a subclass of date.
3621 as_date = date.today()
3622 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003623 self.assertTrue(as_date != as_datetime)
3624 self.assertTrue(as_datetime != as_date)
3625 self.assertTrue(not as_date == as_datetime)
3626 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003627 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3628 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3629 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3630 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3631 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3632 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3633 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3634 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3635
3636 # Neverthelss, comparison should work with the base-class (date)
3637 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003638 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003639 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003640 as_different = as_datetime.replace(day= different_day)
3641 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003642
3643 # And date should compare with other subclasses of date. If a
3644 # subclass wants to stop this, it's up to the subclass to do so.
3645 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3646 self.assertEqual(as_date, date_sc)
3647 self.assertEqual(date_sc, as_date)
3648
3649 # Ditto for datetimes.
3650 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3651 as_date.day, 0, 0, 0)
3652 self.assertEqual(as_datetime, datetime_sc)
3653 self.assertEqual(datetime_sc, as_datetime)
3654
Tim Peters2a799bf2002-12-16 20:18:38 +00003655def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003656 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003657
3658if __name__ == "__main__":
3659 test_main()