blob: bb360012336e2552593264983c61315d44277492 [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
Alexander Belopolskya11d8c02010-07-06 23:19:45 +0000156 def test_repr(self):
157 import datetime
158 for tz in [self.ACDT, self.EST, timezone.utc,
159 timezone.min, timezone.max]:
160 # test round-trip
161 tzrep = repr(tz)
162 self.assertEqual(tz, eval(tzrep))
163
164
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000165 def test_class_members(self):
166 limit = timedelta(hours=23, minutes=59)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000167 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
168 self.assertEqual(timezone.min.utcoffset(None), -limit)
169 self.assertEqual(timezone.max.utcoffset(None), limit)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000170
171
172 def test_constructor(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000173 self.assertEqual(timezone.utc, timezone(timedelta(0)))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000174 # invalid offsets
175 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
176 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
177 self.assertRaises(ValueError, timezone, invalid)
178 self.assertRaises(ValueError, timezone, -invalid)
179
180 with self.assertRaises(TypeError): timezone(None)
181 with self.assertRaises(TypeError): timezone(42)
182 with self.assertRaises(TypeError): timezone(ZERO, None)
183 with self.assertRaises(TypeError): timezone(ZERO, 42)
Alexander Belopolsky5e307de2010-06-23 22:58:49 +0000184 with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000185
186 def test_inheritance(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000187 self.assertIsInstance(timezone.utc, tzinfo)
188 self.assertIsInstance(self.EST, tzinfo)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000189
190 def test_utcoffset(self):
191 dummy = self.DT
192 for h in [0, 1.5, 12]:
193 offset = h * HOUR
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000194 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
195 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000196
197 with self.assertRaises(TypeError): self.EST.utcoffset('')
198 with self.assertRaises(TypeError): self.EST.utcoffset(5)
199
200
201 def test_dst(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000202 self.assertEqual(None, timezone.utc.dst(self.DT))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000203
204 with self.assertRaises(TypeError): self.EST.dst('')
205 with self.assertRaises(TypeError): self.EST.dst(5)
206
207 def test_tzname(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000208 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
209 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
210 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
211 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
212 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000213
214 with self.assertRaises(TypeError): self.EST.tzname('')
215 with self.assertRaises(TypeError): self.EST.tzname(5)
216
217 def test_fromutc(self):
218 with self.assertRaises(ValueError):
219 timezone.utc.fromutc(self.DT)
Alexander Belopolsky5e307de2010-06-23 22:58:49 +0000220 with self.assertRaises(TypeError):
221 timezone.utc.fromutc('not datetime')
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000222 for tz in [self.EST, self.ACDT, Eastern]:
223 utctime = self.DT.replace(tzinfo=tz)
224 local = tz.fromutc(utctime)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000225 self.assertEqual(local - utctime, tz.utcoffset(local))
226 self.assertEqual(local,
227 self.DT.replace(tzinfo=timezone.utc))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000228
229 def test_comparison(self):
230 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
231 self.assertEqual(timezone(HOUR), timezone(HOUR))
232 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
233 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
234 self.assertIn(timezone(ZERO), {timezone(ZERO)})
235
236 def test_aware_datetime(self):
237 # test that timezone instances can be used by datetime
238 t = datetime(1, 1, 1)
239 for tz in [timezone.min, timezone.max, timezone.utc]:
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000240 self.assertEqual(tz.tzname(t),
241 t.replace(tzinfo=tz).tzname())
242 self.assertEqual(tz.utcoffset(t),
243 t.replace(tzinfo=tz).utcoffset())
244 self.assertEqual(tz.dst(t),
245 t.replace(tzinfo=tz).dst())
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000246
Tim Peters2a799bf2002-12-16 20:18:38 +0000247#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000248# Base clase for testing a particular aspect of timedelta, time, date and
249# datetime comparisons.
250
Guido van Rossumd8faa362007-04-27 19:54:29 +0000251class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000252 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
253
254 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
255 # legit constructor.
256
257 def test_harmless_mixed_comparison(self):
258 me = self.theclass(1, 1, 1)
259
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000260 self.assertFalse(me == ())
261 self.assertTrue(me != ())
262 self.assertFalse(() == me)
263 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000264
Benjamin Peterson577473f2010-01-19 00:09:57 +0000265 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000266 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000267
268 def test_harmful_mixed_comparison(self):
269 me = self.theclass(1, 1, 1)
270
271 self.assertRaises(TypeError, lambda: me < ())
272 self.assertRaises(TypeError, lambda: me <= ())
273 self.assertRaises(TypeError, lambda: me > ())
274 self.assertRaises(TypeError, lambda: me >= ())
275
276 self.assertRaises(TypeError, lambda: () < me)
277 self.assertRaises(TypeError, lambda: () <= me)
278 self.assertRaises(TypeError, lambda: () > me)
279 self.assertRaises(TypeError, lambda: () >= me)
280
Tim Peters07534a62003-02-07 22:50:28 +0000281#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000282# timedelta tests
283
Guido van Rossumd8faa362007-04-27 19:54:29 +0000284class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000285
286 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000287
288 def test_constructor(self):
289 eq = self.assertEqual
290 td = timedelta
291
292 # Check keyword args to constructor
293 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
294 milliseconds=0, microseconds=0))
295 eq(td(1), td(days=1))
296 eq(td(0, 1), td(seconds=1))
297 eq(td(0, 0, 1), td(microseconds=1))
298 eq(td(weeks=1), td(days=7))
299 eq(td(days=1), td(hours=24))
300 eq(td(hours=1), td(minutes=60))
301 eq(td(minutes=1), td(seconds=60))
302 eq(td(seconds=1), td(milliseconds=1000))
303 eq(td(milliseconds=1), td(microseconds=1000))
304
305 # Check float args to constructor
306 eq(td(weeks=1.0/7), td(days=1))
307 eq(td(days=1.0/24), td(hours=1))
308 eq(td(hours=1.0/60), td(minutes=1))
309 eq(td(minutes=1.0/60), td(seconds=1))
310 eq(td(seconds=0.001), td(milliseconds=1))
311 eq(td(milliseconds=0.001), td(microseconds=1))
312
313 def test_computations(self):
314 eq = self.assertEqual
315 td = timedelta
316
317 a = td(7) # One week
318 b = td(0, 60) # One minute
319 c = td(0, 0, 1000) # One millisecond
320 eq(a+b+c, td(7, 60, 1000))
321 eq(a-b, td(6, 24*3600 - 60))
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000322 eq(b.__rsub__(a), td(6, 24*3600 - 60))
Tim Peters2a799bf2002-12-16 20:18:38 +0000323 eq(-a, td(-7))
324 eq(+a, td(7))
325 eq(-b, td(-1, 24*3600 - 60))
326 eq(-c, td(-1, 24*3600 - 1, 999000))
327 eq(abs(a), a)
328 eq(abs(-a), a)
329 eq(td(6, 24*3600), a)
330 eq(td(0, 0, 60*1000000), b)
331 eq(a*10, td(70))
332 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000333 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000334 eq(b*10, td(0, 600))
335 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000336 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000337 eq(c*10, td(0, 0, 10000))
338 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000339 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000340 eq(a*-1, -a)
341 eq(b*-2, -b-b)
342 eq(c*-2, -c+-c)
343 eq(b*(60*24), (b*60)*24)
344 eq(b*(60*24), (60*b)*24)
345 eq(c*1000, td(0, 1))
346 eq(1000*c, td(0, 1))
347 eq(a//7, td(1))
348 eq(b//10, td(0, 6))
349 eq(c//1000, td(0, 0, 1))
350 eq(a//10, td(0, 7*24*360))
351 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000352 eq(a/0.5, td(14))
353 eq(b/0.5, td(0, 120))
354 eq(a/7, td(1))
355 eq(b/10, td(0, 6))
356 eq(c/1000, td(0, 0, 1))
357 eq(a/10, td(0, 7*24*360))
358 eq(a/3600000, td(0, 0, 7*24*1000))
359
360 # Multiplication by float
361 us = td(microseconds=1)
362 eq((3*us) * 0.5, 2*us)
363 eq((5*us) * 0.5, 2*us)
364 eq(0.5 * (3*us), 2*us)
365 eq(0.5 * (5*us), 2*us)
366 eq((-3*us) * 0.5, -2*us)
367 eq((-5*us) * 0.5, -2*us)
368
369 # Division by int and float
370 eq((3*us) / 2, 2*us)
371 eq((5*us) / 2, 2*us)
372 eq((-3*us) / 2.0, -2*us)
373 eq((-5*us) / 2.0, -2*us)
374 eq((3*us) / -2, -2*us)
375 eq((5*us) / -2, -2*us)
376 eq((3*us) / -2.0, -2*us)
377 eq((5*us) / -2.0, -2*us)
378 for i in range(-10, 10):
379 eq((i*us/3)//us, round(i/3))
380 for i in range(-10, 10):
381 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000382
383 def test_disallowed_computations(self):
384 a = timedelta(42)
385
Mark Dickinson5c2db372009-12-05 20:28:34 +0000386 # Add/sub ints or floats should be illegal
387 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000388 self.assertRaises(TypeError, lambda: a+i)
389 self.assertRaises(TypeError, lambda: a-i)
390 self.assertRaises(TypeError, lambda: i+a)
391 self.assertRaises(TypeError, lambda: i-a)
392
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000393 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000394 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000395 zero = 0
396 self.assertRaises(TypeError, lambda: zero // a)
397 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000398 self.assertRaises(ZeroDivisionError, lambda: a / zero)
399 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000400 self.assertRaises(TypeError, lambda: a / '')
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000401
402 @requires_IEEE_754
403 def test_disallowed_special(self):
404 a = timedelta(42)
405 self.assertRaises(ValueError, a.__mul__, NAN)
406 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000407
408 def test_basic_attributes(self):
409 days, seconds, us = 1, 7, 31
410 td = timedelta(days, seconds, us)
411 self.assertEqual(td.days, days)
412 self.assertEqual(td.seconds, seconds)
413 self.assertEqual(td.microseconds, us)
414
Antoine Pitroube6859d2009-11-25 23:02:32 +0000415 def test_total_seconds(self):
416 td = timedelta(days=365)
417 self.assertEqual(td.total_seconds(), 31536000.0)
418 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
419 td = timedelta(seconds=total_seconds)
420 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000421 # Issue8644: Test that td.total_seconds() has the same
422 # accuracy as td / timedelta(seconds=1).
423 for ms in [-1, -2, -123]:
424 td = timedelta(microseconds=ms)
425 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000426
Tim Peters2a799bf2002-12-16 20:18:38 +0000427 def test_carries(self):
428 t1 = timedelta(days=100,
429 weeks=-7,
430 hours=-24*(100-49),
431 minutes=-3,
432 seconds=12,
433 microseconds=(3*60 - 12) * 1e6 + 1)
434 t2 = timedelta(microseconds=1)
435 self.assertEqual(t1, t2)
436
437 def test_hash_equality(self):
438 t1 = timedelta(days=100,
439 weeks=-7,
440 hours=-24*(100-49),
441 minutes=-3,
442 seconds=12,
443 microseconds=(3*60 - 12) * 1000000)
444 t2 = timedelta()
445 self.assertEqual(hash(t1), hash(t2))
446
447 t1 += timedelta(weeks=7)
448 t2 += timedelta(days=7*7)
449 self.assertEqual(t1, t2)
450 self.assertEqual(hash(t1), hash(t2))
451
452 d = {t1: 1}
453 d[t2] = 2
454 self.assertEqual(len(d), 1)
455 self.assertEqual(d[t1], 2)
456
457 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000458 args = 12, 34, 56
459 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000460 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000461 green = pickler.dumps(orig, proto)
462 derived = unpickler.loads(green)
463 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000464
465 def test_compare(self):
466 t1 = timedelta(2, 3, 4)
467 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000468 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000469 self.assertTrue(t1 <= t2)
470 self.assertTrue(t1 >= t2)
471 self.assertTrue(not t1 != t2)
472 self.assertTrue(not t1 < t2)
473 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000474
475 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
476 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000477 self.assertTrue(t1 < t2)
478 self.assertTrue(t2 > t1)
479 self.assertTrue(t1 <= t2)
480 self.assertTrue(t2 >= t1)
481 self.assertTrue(t1 != t2)
482 self.assertTrue(t2 != t1)
483 self.assertTrue(not t1 == t2)
484 self.assertTrue(not t2 == t1)
485 self.assertTrue(not t1 > t2)
486 self.assertTrue(not t2 < t1)
487 self.assertTrue(not t1 >= t2)
488 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000489
Tim Peters68124bb2003-02-08 03:46:31 +0000490 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000491 self.assertEqual(t1 == badarg, False)
492 self.assertEqual(t1 != badarg, True)
493 self.assertEqual(badarg == t1, False)
494 self.assertEqual(badarg != t1, True)
495
Tim Peters2a799bf2002-12-16 20:18:38 +0000496 self.assertRaises(TypeError, lambda: t1 <= badarg)
497 self.assertRaises(TypeError, lambda: t1 < badarg)
498 self.assertRaises(TypeError, lambda: t1 > badarg)
499 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000500 self.assertRaises(TypeError, lambda: badarg <= t1)
501 self.assertRaises(TypeError, lambda: badarg < t1)
502 self.assertRaises(TypeError, lambda: badarg > t1)
503 self.assertRaises(TypeError, lambda: badarg >= t1)
504
505 def test_str(self):
506 td = timedelta
507 eq = self.assertEqual
508
509 eq(str(td(1)), "1 day, 0:00:00")
510 eq(str(td(-1)), "-1 day, 0:00:00")
511 eq(str(td(2)), "2 days, 0:00:00")
512 eq(str(td(-2)), "-2 days, 0:00:00")
513
514 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
515 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
516 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
517 "-210 days, 23:12:34")
518
519 eq(str(td(milliseconds=1)), "0:00:00.001000")
520 eq(str(td(microseconds=3)), "0:00:00.000003")
521
522 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
523 microseconds=999999)),
524 "999999999 days, 23:59:59.999999")
525
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000526 def test_repr(self):
527 name = 'datetime.' + self.theclass.__name__
528 self.assertEqual(repr(self.theclass(1)),
529 "%s(1)" % name)
530 self.assertEqual(repr(self.theclass(10, 2)),
531 "%s(10, 2)" % name)
532 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
533 "%s(-10, 2, 400000)" % name)
534
Tim Peters2a799bf2002-12-16 20:18:38 +0000535 def test_roundtrip(self):
536 for td in (timedelta(days=999999999, hours=23, minutes=59,
537 seconds=59, microseconds=999999),
538 timedelta(days=-999999999),
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000539 timedelta(days=-999999999, seconds=1),
Tim Peters2a799bf2002-12-16 20:18:38 +0000540 timedelta(days=1, seconds=2, microseconds=3)):
541
542 # Verify td -> string -> td identity.
543 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000544 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000545 s = s[9:]
546 td2 = eval(s)
547 self.assertEqual(td, td2)
548
549 # Verify identity via reconstructing from pieces.
550 td2 = timedelta(td.days, td.seconds, td.microseconds)
551 self.assertEqual(td, td2)
552
553 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000554 self.assertIsInstance(timedelta.min, timedelta)
555 self.assertIsInstance(timedelta.max, timedelta)
556 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000557 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000558 self.assertEqual(timedelta.min, timedelta(-999999999))
559 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
560 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
561
562 def test_overflow(self):
563 tiny = timedelta.resolution
564
565 td = timedelta.min + tiny
566 td -= tiny # no problem
567 self.assertRaises(OverflowError, td.__sub__, tiny)
568 self.assertRaises(OverflowError, td.__add__, -tiny)
569
570 td = timedelta.max - tiny
571 td += tiny # no problem
572 self.assertRaises(OverflowError, td.__add__, tiny)
573 self.assertRaises(OverflowError, td.__sub__, -tiny)
574
575 self.assertRaises(OverflowError, lambda: -timedelta.max)
576
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000577 day = timedelta(1)
578 self.assertRaises(OverflowError, day.__mul__, 10**9)
579 self.assertRaises(OverflowError, day.__mul__, 1e9)
580 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
581 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
582 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
583
584 @requires_IEEE_754
585 def _test_overflow_special(self):
586 day = timedelta(1)
587 self.assertRaises(OverflowError, day.__mul__, INF)
588 self.assertRaises(OverflowError, day.__mul__, -INF)
589
Tim Peters2a799bf2002-12-16 20:18:38 +0000590 def test_microsecond_rounding(self):
591 td = timedelta
592 eq = self.assertEqual
593
594 # Single-field rounding.
595 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
596 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
597 eq(td(milliseconds=0.6/1000), td(microseconds=1))
598 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
599
600 # Rounding due to contributions from more than one field.
601 us_per_hour = 3600e6
602 us_per_day = us_per_hour * 24
603 eq(td(days=.4/us_per_day), td(0))
604 eq(td(hours=.2/us_per_hour), td(0))
605 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
606
607 eq(td(days=-.4/us_per_day), td(0))
608 eq(td(hours=-.2/us_per_hour), td(0))
609 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
610
611 def test_massive_normalization(self):
612 td = timedelta(microseconds=-1)
613 self.assertEqual((td.days, td.seconds, td.microseconds),
614 (-1, 24*3600-1, 999999))
615
616 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000617 self.assertTrue(timedelta(1))
618 self.assertTrue(timedelta(0, 1))
619 self.assertTrue(timedelta(0, 0, 1))
620 self.assertTrue(timedelta(microseconds=1))
621 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000622
Tim Petersb0c854d2003-05-17 15:57:00 +0000623 def test_subclass_timedelta(self):
624
625 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000626 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000627 def from_td(td):
628 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000629
630 def as_hours(self):
631 sum = (self.days * 24 +
632 self.seconds / 3600.0 +
633 self.microseconds / 3600e6)
634 return round(sum)
635
636 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000637 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000638 self.assertEqual(t1.as_hours(), 24)
639
640 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000641 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000642 self.assertEqual(t2.as_hours(), -25)
643
644 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000645 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000646 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000647 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000648 self.assertEqual(t3.days, t4.days)
649 self.assertEqual(t3.seconds, t4.seconds)
650 self.assertEqual(t3.microseconds, t4.microseconds)
651 self.assertEqual(str(t3), str(t4))
652 self.assertEqual(t4.as_hours(), -1)
653
Mark Dickinson7c186e22010-04-20 22:32:49 +0000654 def test_division(self):
655 t = timedelta(hours=1, minutes=24, seconds=19)
656 second = timedelta(seconds=1)
657 self.assertEqual(t / second, 5059.0)
658 self.assertEqual(t // second, 5059)
659
660 t = timedelta(minutes=2, seconds=30)
661 minute = timedelta(minutes=1)
662 self.assertEqual(t / minute, 2.5)
663 self.assertEqual(t // minute, 2)
664
665 zerotd = timedelta(0)
666 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
667 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
668
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000669 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000670 # note: floor division of a timedelta by an integer *is*
671 # currently permitted.
672
673 def test_remainder(self):
674 t = timedelta(minutes=2, seconds=30)
675 minute = timedelta(minutes=1)
676 r = t % minute
677 self.assertEqual(r, timedelta(seconds=30))
678
679 t = timedelta(minutes=-2, seconds=30)
680 r = t % minute
681 self.assertEqual(r, timedelta(seconds=30))
682
683 zerotd = timedelta(0)
684 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
685
686 self.assertRaises(TypeError, mod, t, 10)
687
688 def test_divmod(self):
689 t = timedelta(minutes=2, seconds=30)
690 minute = timedelta(minutes=1)
691 q, r = divmod(t, minute)
692 self.assertEqual(q, 2)
693 self.assertEqual(r, timedelta(seconds=30))
694
695 t = timedelta(minutes=-2, seconds=30)
696 q, r = divmod(t, minute)
697 self.assertEqual(q, -2)
698 self.assertEqual(r, timedelta(seconds=30))
699
700 zerotd = timedelta(0)
701 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
702
703 self.assertRaises(TypeError, divmod, t, 10)
704
705
Tim Peters2a799bf2002-12-16 20:18:38 +0000706#############################################################################
707# date tests
708
709class TestDateOnly(unittest.TestCase):
710 # Tests here won't pass if also run on datetime objects, so don't
711 # subclass this to test datetimes too.
712
713 def test_delta_non_days_ignored(self):
714 dt = date(2000, 1, 2)
715 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
716 microseconds=5)
717 days = timedelta(delta.days)
718 self.assertEqual(days, timedelta(1))
719
720 dt2 = dt + delta
721 self.assertEqual(dt2, dt + days)
722
723 dt2 = delta + dt
724 self.assertEqual(dt2, dt + days)
725
726 dt2 = dt - delta
727 self.assertEqual(dt2, dt - days)
728
729 delta = -delta
730 days = timedelta(delta.days)
731 self.assertEqual(days, timedelta(-2))
732
733 dt2 = dt + delta
734 self.assertEqual(dt2, dt + days)
735
736 dt2 = delta + dt
737 self.assertEqual(dt2, dt + days)
738
739 dt2 = dt - delta
740 self.assertEqual(dt2, dt - days)
741
Tim Peters604c0132004-06-07 23:04:33 +0000742class SubclassDate(date):
743 sub_var = 1
744
Guido van Rossumd8faa362007-04-27 19:54:29 +0000745class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000746 # Tests here should pass for both dates and datetimes, except for a
747 # few tests that TestDateTime overrides.
748
749 theclass = date
750
751 def test_basic_attributes(self):
752 dt = self.theclass(2002, 3, 1)
753 self.assertEqual(dt.year, 2002)
754 self.assertEqual(dt.month, 3)
755 self.assertEqual(dt.day, 1)
756
757 def test_roundtrip(self):
758 for dt in (self.theclass(1, 2, 3),
759 self.theclass.today()):
760 # Verify dt -> string -> date identity.
761 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000762 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000763 s = s[9:]
764 dt2 = eval(s)
765 self.assertEqual(dt, dt2)
766
767 # Verify identity via reconstructing from pieces.
768 dt2 = self.theclass(dt.year, dt.month, dt.day)
769 self.assertEqual(dt, dt2)
770
771 def test_ordinal_conversions(self):
772 # Check some fixed values.
773 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
774 (1, 12, 31, 365),
775 (2, 1, 1, 366),
776 # first example from "Calendrical Calculations"
777 (1945, 11, 12, 710347)]:
778 d = self.theclass(y, m, d)
779 self.assertEqual(n, d.toordinal())
780 fromord = self.theclass.fromordinal(n)
781 self.assertEqual(d, fromord)
782 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000783 # if we're checking something fancier than a date, verify
784 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000785 self.assertEqual(fromord.hour, 0)
786 self.assertEqual(fromord.minute, 0)
787 self.assertEqual(fromord.second, 0)
788 self.assertEqual(fromord.microsecond, 0)
789
Tim Peters0bf60bd2003-01-08 20:40:01 +0000790 # Check first and last days of year spottily across the whole
791 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000792 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000793 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
794 d = self.theclass(year, 1, 1)
795 n = d.toordinal()
796 d2 = self.theclass.fromordinal(n)
797 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000798 # Verify that moving back a day gets to the end of year-1.
799 if year > 1:
800 d = self.theclass.fromordinal(n-1)
801 d2 = self.theclass(year-1, 12, 31)
802 self.assertEqual(d, d2)
803 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000804
805 # Test every day in a leap-year and a non-leap year.
806 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
807 for year, isleap in (2000, True), (2002, False):
808 n = self.theclass(year, 1, 1).toordinal()
809 for month, maxday in zip(range(1, 13), dim):
810 if month == 2 and isleap:
811 maxday += 1
812 for day in range(1, maxday+1):
813 d = self.theclass(year, month, day)
814 self.assertEqual(d.toordinal(), n)
815 self.assertEqual(d, self.theclass.fromordinal(n))
816 n += 1
817
818 def test_extreme_ordinals(self):
819 a = self.theclass.min
820 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
821 aord = a.toordinal()
822 b = a.fromordinal(aord)
823 self.assertEqual(a, b)
824
825 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
826
827 b = a + timedelta(days=1)
828 self.assertEqual(b.toordinal(), aord + 1)
829 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
830
831 a = self.theclass.max
832 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
833 aord = a.toordinal()
834 b = a.fromordinal(aord)
835 self.assertEqual(a, b)
836
837 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
838
839 b = a - timedelta(days=1)
840 self.assertEqual(b.toordinal(), aord - 1)
841 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
842
843 def test_bad_constructor_arguments(self):
844 # bad years
845 self.theclass(MINYEAR, 1, 1) # no exception
846 self.theclass(MAXYEAR, 1, 1) # no exception
847 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
848 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
849 # bad months
850 self.theclass(2000, 1, 1) # no exception
851 self.theclass(2000, 12, 1) # no exception
852 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
853 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
854 # bad days
855 self.theclass(2000, 2, 29) # no exception
856 self.theclass(2004, 2, 29) # no exception
857 self.theclass(2400, 2, 29) # no exception
858 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
859 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
860 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
861 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
862 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
863 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
864
865 def test_hash_equality(self):
866 d = self.theclass(2000, 12, 31)
867 # same thing
868 e = self.theclass(2000, 12, 31)
869 self.assertEqual(d, e)
870 self.assertEqual(hash(d), hash(e))
871
872 dic = {d: 1}
873 dic[e] = 2
874 self.assertEqual(len(dic), 1)
875 self.assertEqual(dic[d], 2)
876 self.assertEqual(dic[e], 2)
877
878 d = self.theclass(2001, 1, 1)
879 # same thing
880 e = self.theclass(2001, 1, 1)
881 self.assertEqual(d, e)
882 self.assertEqual(hash(d), hash(e))
883
884 dic = {d: 1}
885 dic[e] = 2
886 self.assertEqual(len(dic), 1)
887 self.assertEqual(dic[d], 2)
888 self.assertEqual(dic[e], 2)
889
890 def test_computations(self):
891 a = self.theclass(2002, 1, 31)
892 b = self.theclass(1956, 1, 31)
Alexander Belopolskyd87e9322010-07-05 17:57:31 +0000893 c = self.theclass(2001,2,1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000894
895 diff = a-b
896 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
897 self.assertEqual(diff.seconds, 0)
898 self.assertEqual(diff.microseconds, 0)
899
900 day = timedelta(1)
901 week = timedelta(7)
902 a = self.theclass(2002, 3, 2)
903 self.assertEqual(a + day, self.theclass(2002, 3, 3))
904 self.assertEqual(day + a, self.theclass(2002, 3, 3))
905 self.assertEqual(a - day, self.theclass(2002, 3, 1))
906 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
907 self.assertEqual(a + week, self.theclass(2002, 3, 9))
908 self.assertEqual(a - week, self.theclass(2002, 2, 23))
909 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
910 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
911 self.assertEqual((a + week) - a, week)
912 self.assertEqual((a + day) - a, day)
913 self.assertEqual((a - week) - a, -week)
914 self.assertEqual((a - day) - a, -day)
915 self.assertEqual(a - (a + week), -week)
916 self.assertEqual(a - (a + day), -day)
917 self.assertEqual(a - (a - week), week)
918 self.assertEqual(a - (a - day), day)
Alexander Belopolskyd87e9322010-07-05 17:57:31 +0000919 self.assertEqual(c - (c - day), day)
Tim Peters2a799bf2002-12-16 20:18:38 +0000920
Mark Dickinson5c2db372009-12-05 20:28:34 +0000921 # Add/sub ints or floats should be illegal
922 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000923 self.assertRaises(TypeError, lambda: a+i)
924 self.assertRaises(TypeError, lambda: a-i)
925 self.assertRaises(TypeError, lambda: i+a)
926 self.assertRaises(TypeError, lambda: i-a)
927
928 # delta - date is senseless.
929 self.assertRaises(TypeError, lambda: day - a)
930 # mixing date and (delta or date) via * or // is senseless
931 self.assertRaises(TypeError, lambda: day * a)
932 self.assertRaises(TypeError, lambda: a * day)
933 self.assertRaises(TypeError, lambda: day // a)
934 self.assertRaises(TypeError, lambda: a // day)
935 self.assertRaises(TypeError, lambda: a * a)
936 self.assertRaises(TypeError, lambda: a // a)
937 # date + date is senseless
938 self.assertRaises(TypeError, lambda: a + a)
939
940 def test_overflow(self):
941 tiny = self.theclass.resolution
942
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000943 for delta in [tiny, timedelta(1), timedelta(2)]:
944 dt = self.theclass.min + delta
945 dt -= delta # no problem
946 self.assertRaises(OverflowError, dt.__sub__, delta)
947 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000948
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000949 dt = self.theclass.max - delta
950 dt += delta # no problem
951 self.assertRaises(OverflowError, dt.__add__, delta)
952 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000953
954 def test_fromtimestamp(self):
955 import time
956
957 # Try an arbitrary fixed value.
958 year, month, day = 1999, 9, 19
959 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
960 d = self.theclass.fromtimestamp(ts)
961 self.assertEqual(d.year, year)
962 self.assertEqual(d.month, month)
963 self.assertEqual(d.day, day)
964
Tim Peters1b6f7a92004-06-20 02:50:16 +0000965 def test_insane_fromtimestamp(self):
966 # It's possible that some platform maps time_t to double,
967 # and that this test will fail there. This test should
968 # exempt such platforms (provided they return reasonable
969 # results!).
970 for insane in -1e200, 1e200:
971 self.assertRaises(ValueError, self.theclass.fromtimestamp,
972 insane)
973
Tim Peters2a799bf2002-12-16 20:18:38 +0000974 def test_today(self):
975 import time
976
977 # We claim that today() is like fromtimestamp(time.time()), so
978 # prove it.
979 for dummy in range(3):
980 today = self.theclass.today()
981 ts = time.time()
982 todayagain = self.theclass.fromtimestamp(ts)
983 if today == todayagain:
984 break
985 # There are several legit reasons that could fail:
986 # 1. It recently became midnight, between the today() and the
987 # time() calls.
988 # 2. The platform time() has such fine resolution that we'll
989 # never get the same value twice.
990 # 3. The platform time() has poor resolution, and we just
991 # happened to call today() right before a resolution quantum
992 # boundary.
993 # 4. The system clock got fiddled between calls.
994 # In any case, wait a little while and try again.
995 time.sleep(0.1)
996
997 # It worked or it didn't. If it didn't, assume it's reason #2, and
998 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000999 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +00001000 abs(todayagain - today) < timedelta(seconds=0.5))
1001
1002 def test_weekday(self):
1003 for i in range(7):
1004 # March 4, 2002 is a Monday
1005 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
1006 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
1007 # January 2, 1956 is a Monday
1008 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
1009 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
1010
1011 def test_isocalendar(self):
1012 # Check examples from
1013 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1014 for i in range(7):
1015 d = self.theclass(2003, 12, 22+i)
1016 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1017 d = self.theclass(2003, 12, 29) + timedelta(i)
1018 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1019 d = self.theclass(2004, 1, 5+i)
1020 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1021 d = self.theclass(2009, 12, 21+i)
1022 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1023 d = self.theclass(2009, 12, 28) + timedelta(i)
1024 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1025 d = self.theclass(2010, 1, 4+i)
1026 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1027
1028 def test_iso_long_years(self):
1029 # Calculate long ISO years and compare to table from
1030 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1031 ISO_LONG_YEARS_TABLE = """
1032 4 32 60 88
1033 9 37 65 93
1034 15 43 71 99
1035 20 48 76
1036 26 54 82
1037
1038 105 133 161 189
1039 111 139 167 195
1040 116 144 172
1041 122 150 178
1042 128 156 184
1043
1044 201 229 257 285
1045 207 235 263 291
1046 212 240 268 296
1047 218 246 274
1048 224 252 280
1049
1050 303 331 359 387
1051 308 336 364 392
1052 314 342 370 398
1053 320 348 376
1054 325 353 381
1055 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001056 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001057 L = []
1058 for i in range(400):
1059 d = self.theclass(2000+i, 12, 31)
1060 d1 = self.theclass(1600+i, 12, 31)
1061 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1062 if d.isocalendar()[1] == 53:
1063 L.append(i)
1064 self.assertEqual(L, iso_long_years)
1065
1066 def test_isoformat(self):
1067 t = self.theclass(2, 3, 2)
1068 self.assertEqual(t.isoformat(), "0002-03-02")
1069
1070 def test_ctime(self):
1071 t = self.theclass(2002, 3, 2)
1072 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1073
1074 def test_strftime(self):
1075 t = self.theclass(2005, 3, 2)
1076 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001077 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001078 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001079
1080 self.assertRaises(TypeError, t.strftime) # needs an arg
1081 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1082 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1083
Georg Brandlf78e02b2008-06-10 17:40:04 +00001084 # test that unicode input is allowed (issue 2782)
1085 self.assertEqual(t.strftime("%m"), "03")
1086
Tim Peters2a799bf2002-12-16 20:18:38 +00001087 # A naive object replaces %z and %Z w/ empty strings.
1088 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1089
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001090 #make sure that invalid format specifiers are handled correctly
1091 #self.assertRaises(ValueError, t.strftime, "%e")
1092 #self.assertRaises(ValueError, t.strftime, "%")
1093 #self.assertRaises(ValueError, t.strftime, "%#")
1094
1095 #oh well, some systems just ignore those invalid ones.
1096 #at least, excercise them to make sure that no crashes
1097 #are generated
1098 for f in ["%e", "%", "%#"]:
1099 try:
1100 t.strftime(f)
1101 except ValueError:
1102 pass
1103
1104 #check that this standard extension works
1105 t.strftime("%f")
1106
Georg Brandlf78e02b2008-06-10 17:40:04 +00001107
Eric Smith1ba31142007-09-11 18:06:02 +00001108 def test_format(self):
1109 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001110 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001111
1112 # check that a derived class's __str__() gets called
1113 class A(self.theclass):
1114 def __str__(self):
1115 return 'A'
1116 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001117 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001118
1119 # check that a derived class's strftime gets called
1120 class B(self.theclass):
1121 def strftime(self, format_spec):
1122 return 'B'
1123 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001124 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001125
1126 for fmt in ["m:%m d:%d y:%y",
1127 "m:%m d:%d y:%y H:%H M:%M S:%S",
1128 "%z %Z",
1129 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001130 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1131 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1132 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001133
Tim Peters2a799bf2002-12-16 20:18:38 +00001134 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001135 # XXX: Should min and max respect subclassing?
1136 if issubclass(self.theclass, datetime):
1137 expected_class = datetime
1138 else:
1139 expected_class = date
1140 self.assertIsInstance(self.theclass.min, expected_class)
1141 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001142 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001143 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001144
1145 def test_extreme_timedelta(self):
1146 big = self.theclass.max - self.theclass.min
1147 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1148 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1149 # n == 315537897599999999 ~= 2**58.13
1150 justasbig = timedelta(0, 0, n)
1151 self.assertEqual(big, justasbig)
1152 self.assertEqual(self.theclass.min + big, self.theclass.max)
1153 self.assertEqual(self.theclass.max - big, self.theclass.min)
1154
1155 def test_timetuple(self):
1156 for i in range(7):
1157 # January 2, 1956 is a Monday (0)
1158 d = self.theclass(1956, 1, 2+i)
1159 t = d.timetuple()
1160 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1161 # February 1, 1956 is a Wednesday (2)
1162 d = self.theclass(1956, 2, 1+i)
1163 t = d.timetuple()
1164 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1165 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1166 # of the year.
1167 d = self.theclass(1956, 3, 1+i)
1168 t = d.timetuple()
1169 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1170 self.assertEqual(t.tm_year, 1956)
1171 self.assertEqual(t.tm_mon, 3)
1172 self.assertEqual(t.tm_mday, 1+i)
1173 self.assertEqual(t.tm_hour, 0)
1174 self.assertEqual(t.tm_min, 0)
1175 self.assertEqual(t.tm_sec, 0)
1176 self.assertEqual(t.tm_wday, (3+i)%7)
1177 self.assertEqual(t.tm_yday, 61+i)
1178 self.assertEqual(t.tm_isdst, -1)
1179
1180 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001181 args = 6, 7, 23
1182 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001183 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001184 green = pickler.dumps(orig, proto)
1185 derived = unpickler.loads(green)
1186 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001187
1188 def test_compare(self):
1189 t1 = self.theclass(2, 3, 4)
1190 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001191 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001192 self.assertTrue(t1 <= t2)
1193 self.assertTrue(t1 >= t2)
1194 self.assertTrue(not t1 != t2)
1195 self.assertTrue(not t1 < t2)
1196 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001197
1198 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1199 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001200 self.assertTrue(t1 < t2)
1201 self.assertTrue(t2 > t1)
1202 self.assertTrue(t1 <= t2)
1203 self.assertTrue(t2 >= t1)
1204 self.assertTrue(t1 != t2)
1205 self.assertTrue(t2 != t1)
1206 self.assertTrue(not t1 == t2)
1207 self.assertTrue(not t2 == t1)
1208 self.assertTrue(not t1 > t2)
1209 self.assertTrue(not t2 < t1)
1210 self.assertTrue(not t1 >= t2)
1211 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001212
Tim Peters68124bb2003-02-08 03:46:31 +00001213 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001214 self.assertEqual(t1 == badarg, False)
1215 self.assertEqual(t1 != badarg, True)
1216 self.assertEqual(badarg == t1, False)
1217 self.assertEqual(badarg != t1, True)
1218
Tim Peters2a799bf2002-12-16 20:18:38 +00001219 self.assertRaises(TypeError, lambda: t1 < badarg)
1220 self.assertRaises(TypeError, lambda: t1 > badarg)
1221 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001222 self.assertRaises(TypeError, lambda: badarg <= t1)
1223 self.assertRaises(TypeError, lambda: badarg < t1)
1224 self.assertRaises(TypeError, lambda: badarg > t1)
1225 self.assertRaises(TypeError, lambda: badarg >= t1)
1226
Tim Peters8d81a012003-01-24 22:36:34 +00001227 def test_mixed_compare(self):
1228 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001229
1230 # Our class can be compared for equality to other classes
1231 self.assertEqual(our == 1, False)
1232 self.assertEqual(1 == our, False)
1233 self.assertEqual(our != 1, True)
1234 self.assertEqual(1 != our, True)
1235
1236 # But the ordering is undefined
1237 self.assertRaises(TypeError, lambda: our < 1)
1238 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001239
Guido van Rossum19960592006-08-24 17:29:38 +00001240 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001241
Guido van Rossum19960592006-08-24 17:29:38 +00001242 class SomeClass:
1243 pass
1244
1245 their = SomeClass()
1246 self.assertEqual(our == their, False)
1247 self.assertEqual(their == our, False)
1248 self.assertEqual(our != their, True)
1249 self.assertEqual(their != our, True)
1250 self.assertRaises(TypeError, lambda: our < their)
1251 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001252
Guido van Rossum19960592006-08-24 17:29:38 +00001253 # However, if the other class explicitly defines ordering
1254 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001255
Guido van Rossum19960592006-08-24 17:29:38 +00001256 class LargerThanAnything:
1257 def __lt__(self, other):
1258 return False
1259 def __le__(self, other):
1260 return isinstance(other, LargerThanAnything)
1261 def __eq__(self, other):
1262 return isinstance(other, LargerThanAnything)
1263 def __ne__(self, other):
1264 return not isinstance(other, LargerThanAnything)
1265 def __gt__(self, other):
1266 return not isinstance(other, LargerThanAnything)
1267 def __ge__(self, other):
1268 return True
1269
1270 their = LargerThanAnything()
1271 self.assertEqual(our == their, False)
1272 self.assertEqual(their == our, False)
1273 self.assertEqual(our != their, True)
1274 self.assertEqual(their != our, True)
1275 self.assertEqual(our < their, True)
1276 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001277
Tim Peters2a799bf2002-12-16 20:18:38 +00001278 def test_bool(self):
1279 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001280 self.assertTrue(self.theclass.min)
1281 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001282
Guido van Rossum04110fb2007-08-24 16:32:05 +00001283 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001284 # For nasty technical reasons, we can't handle years before 1900.
1285 cls = self.theclass
1286 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1287 for y in 1, 49, 51, 99, 100, 1000, 1899:
1288 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001289
1290 def test_replace(self):
1291 cls = self.theclass
1292 args = [1, 2, 3]
1293 base = cls(*args)
1294 self.assertEqual(base, base.replace())
1295
1296 i = 0
1297 for name, newval in (("year", 2),
1298 ("month", 3),
1299 ("day", 4)):
1300 newargs = args[:]
1301 newargs[i] = newval
1302 expected = cls(*newargs)
1303 got = base.replace(**{name: newval})
1304 self.assertEqual(expected, got)
1305 i += 1
1306
1307 # Out of bounds.
1308 base = cls(2000, 2, 29)
1309 self.assertRaises(ValueError, base.replace, year=2001)
1310
Tim Petersa98924a2003-05-17 05:55:19 +00001311 def test_subclass_date(self):
1312
1313 class C(self.theclass):
1314 theAnswer = 42
1315
1316 def __new__(cls, *args, **kws):
1317 temp = kws.copy()
1318 extra = temp.pop('extra')
1319 result = self.theclass.__new__(cls, *args, **temp)
1320 result.extra = extra
1321 return result
1322
1323 def newmeth(self, start):
1324 return start + self.year + self.month
1325
1326 args = 2003, 4, 14
1327
1328 dt1 = self.theclass(*args)
1329 dt2 = C(*args, **{'extra': 7})
1330
1331 self.assertEqual(dt2.__class__, C)
1332 self.assertEqual(dt2.theAnswer, 42)
1333 self.assertEqual(dt2.extra, 7)
1334 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1335 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1336
Tim Peters604c0132004-06-07 23:04:33 +00001337 def test_pickling_subclass_date(self):
1338
1339 args = 6, 7, 23
1340 orig = SubclassDate(*args)
1341 for pickler, unpickler, proto in pickle_choices:
1342 green = pickler.dumps(orig, proto)
1343 derived = unpickler.loads(green)
1344 self.assertEqual(orig, derived)
1345
Tim Peters3f606292004-03-21 23:38:41 +00001346 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001347 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001348 # This is a low-overhead backdoor. A user can (by intent or
1349 # mistake) pass a string directly, which (if it's the right length)
1350 # will get treated like a pickle, and bypass the normal sanity
1351 # checks in the constructor. This can create insane objects.
1352 # The constructor doesn't want to burn the time to validate all
1353 # fields, but does check the month field. This stops, e.g.,
1354 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001355 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001356 if not issubclass(self.theclass, datetime):
1357 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001358 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001359 self.assertRaises(TypeError, self.theclass,
1360 base[:2] + month_byte + base[3:])
Alexander Belopolskyd87e9322010-07-05 17:57:31 +00001361 # Good bytes, but bad tzinfo:
1362 self.assertRaises(TypeError, self.theclass,
1363 bytes([1] * len(base)), 'EST')
1364
Tim Peters3f606292004-03-21 23:38:41 +00001365 for ord_byte in range(1, 13):
1366 # This shouldn't blow up because of the month byte alone. If
1367 # the implementation changes to do more-careful checking, it may
1368 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001369 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001370
Tim Peters2a799bf2002-12-16 20:18:38 +00001371#############################################################################
1372# datetime tests
1373
Tim Peters604c0132004-06-07 23:04:33 +00001374class SubclassDatetime(datetime):
1375 sub_var = 1
1376
Tim Peters2a799bf2002-12-16 20:18:38 +00001377class TestDateTime(TestDate):
1378
1379 theclass = datetime
1380
1381 def test_basic_attributes(self):
1382 dt = self.theclass(2002, 3, 1, 12, 0)
1383 self.assertEqual(dt.year, 2002)
1384 self.assertEqual(dt.month, 3)
1385 self.assertEqual(dt.day, 1)
1386 self.assertEqual(dt.hour, 12)
1387 self.assertEqual(dt.minute, 0)
1388 self.assertEqual(dt.second, 0)
1389 self.assertEqual(dt.microsecond, 0)
1390
1391 def test_basic_attributes_nonzero(self):
1392 # Make sure all attributes are non-zero so bugs in
1393 # bit-shifting access show up.
1394 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1395 self.assertEqual(dt.year, 2002)
1396 self.assertEqual(dt.month, 3)
1397 self.assertEqual(dt.day, 1)
1398 self.assertEqual(dt.hour, 12)
1399 self.assertEqual(dt.minute, 59)
1400 self.assertEqual(dt.second, 59)
1401 self.assertEqual(dt.microsecond, 8000)
1402
1403 def test_roundtrip(self):
1404 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1405 self.theclass.now()):
1406 # Verify dt -> string -> datetime identity.
1407 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001408 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001409 s = s[9:]
1410 dt2 = eval(s)
1411 self.assertEqual(dt, dt2)
1412
1413 # Verify identity via reconstructing from pieces.
1414 dt2 = self.theclass(dt.year, dt.month, dt.day,
1415 dt.hour, dt.minute, dt.second,
1416 dt.microsecond)
1417 self.assertEqual(dt, dt2)
1418
1419 def test_isoformat(self):
1420 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1421 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1422 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1423 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001424 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001425 # str is ISO format with the separator forced to a blank.
1426 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1427
1428 t = self.theclass(2, 3, 2)
1429 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1430 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1431 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1432 # str is ISO format with the separator forced to a blank.
1433 self.assertEqual(str(t), "0002-03-02 00:00:00")
1434
Eric Smith1ba31142007-09-11 18:06:02 +00001435 def test_format(self):
1436 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001437 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001438
1439 # check that a derived class's __str__() gets called
1440 class A(self.theclass):
1441 def __str__(self):
1442 return 'A'
1443 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001444 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001445
1446 # check that a derived class's strftime gets called
1447 class B(self.theclass):
1448 def strftime(self, format_spec):
1449 return 'B'
1450 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001451 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001452
1453 for fmt in ["m:%m d:%d y:%y",
1454 "m:%m d:%d y:%y H:%H M:%M S:%S",
1455 "%z %Z",
1456 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001457 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1458 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1459 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001460
Tim Peters2a799bf2002-12-16 20:18:38 +00001461 def test_more_ctime(self):
1462 # Test fields that TestDate doesn't touch.
1463 import time
1464
1465 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1466 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1467 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1468 # out. The difference is that t.ctime() produces " 2" for the day,
1469 # but platform ctime() produces "02" for the day. According to
1470 # C99, t.ctime() is correct here.
1471 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1472
1473 # So test a case where that difference doesn't matter.
1474 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1475 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1476
1477 def test_tz_independent_comparing(self):
1478 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1479 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1480 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1481 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001482 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001483
1484 # Make sure comparison doesn't forget microseconds, and isn't done
1485 # via comparing a float timestamp (an IEEE double doesn't have enough
1486 # precision to span microsecond resolution across years 1 thru 9999,
1487 # so comparing via timestamp necessarily calls some distinct values
1488 # equal).
1489 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1490 us = timedelta(microseconds=1)
1491 dt2 = dt1 + us
1492 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001493 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001494
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001495 def test_strftime_with_bad_tzname_replace(self):
1496 # verify ok if tzinfo.tzname().replace() returns a non-string
1497 class MyTzInfo(FixedOffset):
1498 def tzname(self, dt):
1499 class MyStr(str):
1500 def replace(self, *args):
1501 return None
1502 return MyStr('name')
1503 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1504 self.assertRaises(TypeError, t.strftime, '%Z')
1505
Tim Peters2a799bf2002-12-16 20:18:38 +00001506 def test_bad_constructor_arguments(self):
1507 # bad years
1508 self.theclass(MINYEAR, 1, 1) # no exception
1509 self.theclass(MAXYEAR, 1, 1) # no exception
1510 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1511 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1512 # bad months
1513 self.theclass(2000, 1, 1) # no exception
1514 self.theclass(2000, 12, 1) # no exception
1515 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1516 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1517 # bad days
1518 self.theclass(2000, 2, 29) # no exception
1519 self.theclass(2004, 2, 29) # no exception
1520 self.theclass(2400, 2, 29) # no exception
1521 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1522 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1523 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1524 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1525 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1526 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1527 # bad hours
1528 self.theclass(2000, 1, 31, 0) # no exception
1529 self.theclass(2000, 1, 31, 23) # no exception
1530 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1531 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1532 # bad minutes
1533 self.theclass(2000, 1, 31, 23, 0) # no exception
1534 self.theclass(2000, 1, 31, 23, 59) # no exception
1535 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1536 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1537 # bad seconds
1538 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1539 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1540 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1541 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1542 # bad microseconds
1543 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1544 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1545 self.assertRaises(ValueError, self.theclass,
1546 2000, 1, 31, 23, 59, 59, -1)
1547 self.assertRaises(ValueError, self.theclass,
1548 2000, 1, 31, 23, 59, 59,
1549 1000000)
1550
1551 def test_hash_equality(self):
1552 d = self.theclass(2000, 12, 31, 23, 30, 17)
1553 e = self.theclass(2000, 12, 31, 23, 30, 17)
1554 self.assertEqual(d, e)
1555 self.assertEqual(hash(d), hash(e))
1556
1557 dic = {d: 1}
1558 dic[e] = 2
1559 self.assertEqual(len(dic), 1)
1560 self.assertEqual(dic[d], 2)
1561 self.assertEqual(dic[e], 2)
1562
1563 d = self.theclass(2001, 1, 1, 0, 5, 17)
1564 e = self.theclass(2001, 1, 1, 0, 5, 17)
1565 self.assertEqual(d, e)
1566 self.assertEqual(hash(d), hash(e))
1567
1568 dic = {d: 1}
1569 dic[e] = 2
1570 self.assertEqual(len(dic), 1)
1571 self.assertEqual(dic[d], 2)
1572 self.assertEqual(dic[e], 2)
1573
1574 def test_computations(self):
1575 a = self.theclass(2002, 1, 31)
1576 b = self.theclass(1956, 1, 31)
1577 diff = a-b
1578 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1579 self.assertEqual(diff.seconds, 0)
1580 self.assertEqual(diff.microseconds, 0)
1581 a = self.theclass(2002, 3, 2, 17, 6)
1582 millisec = timedelta(0, 0, 1000)
1583 hour = timedelta(0, 3600)
1584 day = timedelta(1)
1585 week = timedelta(7)
1586 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1587 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1588 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1589 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1590 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1591 self.assertEqual(a - hour, a + -hour)
1592 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1593 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1594 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1595 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1596 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1597 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1598 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1599 self.assertEqual((a + week) - a, week)
1600 self.assertEqual((a + day) - a, day)
1601 self.assertEqual((a + hour) - a, hour)
1602 self.assertEqual((a + millisec) - a, millisec)
1603 self.assertEqual((a - week) - a, -week)
1604 self.assertEqual((a - day) - a, -day)
1605 self.assertEqual((a - hour) - a, -hour)
1606 self.assertEqual((a - millisec) - a, -millisec)
1607 self.assertEqual(a - (a + week), -week)
1608 self.assertEqual(a - (a + day), -day)
1609 self.assertEqual(a - (a + hour), -hour)
1610 self.assertEqual(a - (a + millisec), -millisec)
1611 self.assertEqual(a - (a - week), week)
1612 self.assertEqual(a - (a - day), day)
1613 self.assertEqual(a - (a - hour), hour)
1614 self.assertEqual(a - (a - millisec), millisec)
1615 self.assertEqual(a + (week + day + hour + millisec),
1616 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1617 self.assertEqual(a + (week + day + hour + millisec),
1618 (((a + week) + day) + hour) + millisec)
1619 self.assertEqual(a - (week + day + hour + millisec),
1620 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1621 self.assertEqual(a - (week + day + hour + millisec),
1622 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001623 # Add/sub ints or floats should be illegal
1624 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001625 self.assertRaises(TypeError, lambda: a+i)
1626 self.assertRaises(TypeError, lambda: a-i)
1627 self.assertRaises(TypeError, lambda: i+a)
1628 self.assertRaises(TypeError, lambda: i-a)
1629
1630 # delta - datetime is senseless.
1631 self.assertRaises(TypeError, lambda: day - a)
1632 # mixing datetime and (delta or datetime) via * or // is senseless
1633 self.assertRaises(TypeError, lambda: day * a)
1634 self.assertRaises(TypeError, lambda: a * day)
1635 self.assertRaises(TypeError, lambda: day // a)
1636 self.assertRaises(TypeError, lambda: a // day)
1637 self.assertRaises(TypeError, lambda: a * a)
1638 self.assertRaises(TypeError, lambda: a // a)
1639 # datetime + datetime is senseless
1640 self.assertRaises(TypeError, lambda: a + a)
1641
1642 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001643 args = 6, 7, 23, 20, 59, 1, 64**2
1644 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001645 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001646 green = pickler.dumps(orig, proto)
1647 derived = unpickler.loads(green)
1648 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001649
Guido van Rossum275666f2003-02-07 21:49:01 +00001650 def test_more_pickling(self):
1651 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1652 s = pickle.dumps(a)
1653 b = pickle.loads(s)
1654 self.assertEqual(b.year, 2003)
1655 self.assertEqual(b.month, 2)
1656 self.assertEqual(b.day, 7)
1657
Tim Peters604c0132004-06-07 23:04:33 +00001658 def test_pickling_subclass_datetime(self):
1659 args = 6, 7, 23, 20, 59, 1, 64**2
1660 orig = SubclassDatetime(*args)
1661 for pickler, unpickler, proto in pickle_choices:
1662 green = pickler.dumps(orig, proto)
1663 derived = unpickler.loads(green)
1664 self.assertEqual(orig, derived)
1665
Tim Peters2a799bf2002-12-16 20:18:38 +00001666 def test_more_compare(self):
1667 # The test_compare() inherited from TestDate covers the error cases.
1668 # We just want to test lexicographic ordering on the members datetime
1669 # has that date lacks.
1670 args = [2000, 11, 29, 20, 58, 16, 999998]
1671 t1 = self.theclass(*args)
1672 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001673 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001674 self.assertTrue(t1 <= t2)
1675 self.assertTrue(t1 >= t2)
1676 self.assertTrue(not t1 != t2)
1677 self.assertTrue(not t1 < t2)
1678 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001679
1680 for i in range(len(args)):
1681 newargs = args[:]
1682 newargs[i] = args[i] + 1
1683 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001684 self.assertTrue(t1 < t2)
1685 self.assertTrue(t2 > t1)
1686 self.assertTrue(t1 <= t2)
1687 self.assertTrue(t2 >= t1)
1688 self.assertTrue(t1 != t2)
1689 self.assertTrue(t2 != t1)
1690 self.assertTrue(not t1 == t2)
1691 self.assertTrue(not t2 == t1)
1692 self.assertTrue(not t1 > t2)
1693 self.assertTrue(not t2 < t1)
1694 self.assertTrue(not t1 >= t2)
1695 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001696
1697
1698 # A helper for timestamp constructor tests.
1699 def verify_field_equality(self, expected, got):
1700 self.assertEqual(expected.tm_year, got.year)
1701 self.assertEqual(expected.tm_mon, got.month)
1702 self.assertEqual(expected.tm_mday, got.day)
1703 self.assertEqual(expected.tm_hour, got.hour)
1704 self.assertEqual(expected.tm_min, got.minute)
1705 self.assertEqual(expected.tm_sec, got.second)
1706
1707 def test_fromtimestamp(self):
1708 import time
1709
1710 ts = time.time()
1711 expected = time.localtime(ts)
1712 got = self.theclass.fromtimestamp(ts)
1713 self.verify_field_equality(expected, got)
1714
1715 def test_utcfromtimestamp(self):
1716 import time
1717
1718 ts = time.time()
1719 expected = time.gmtime(ts)
1720 got = self.theclass.utcfromtimestamp(ts)
1721 self.verify_field_equality(expected, got)
1722
Thomas Wouters477c8d52006-05-27 19:21:47 +00001723 def test_microsecond_rounding(self):
1724 # Test whether fromtimestamp "rounds up" floats that are less
1725 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001726 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1727 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001728
Tim Peters1b6f7a92004-06-20 02:50:16 +00001729 def test_insane_fromtimestamp(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.fromtimestamp,
1736 insane)
1737
1738 def test_insane_utcfromtimestamp(self):
1739 # It's possible that some platform maps time_t to double,
1740 # and that this test will fail there. This test should
1741 # exempt such platforms (provided they return reasonable
1742 # results!).
1743 for insane in -1e200, 1e200:
1744 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1745 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001746 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001747 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001748 # The result is tz-dependent; at least test that this doesn't
1749 # fail (like it did before bug 1646728 was fixed).
1750 self.theclass.fromtimestamp(-1.05)
1751
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001752 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001753 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001754 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001755 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001756
Tim Peters2a799bf2002-12-16 20:18:38 +00001757 def test_utcnow(self):
1758 import time
1759
1760 # Call it a success if utcnow() and utcfromtimestamp() are within
1761 # a second of each other.
1762 tolerance = timedelta(seconds=1)
1763 for dummy in range(3):
1764 from_now = self.theclass.utcnow()
1765 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1766 if abs(from_timestamp - from_now) <= tolerance:
1767 break
1768 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001769 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001770
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001771 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001772 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001773
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001774 string = '2004-12-01 13:02:47.197'
1775 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001776 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001777 got = self.theclass.strptime(string, format)
1778 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001779 self.assertIs(type(expected), self.theclass)
1780 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001781
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001782 strptime = self.theclass.strptime
1783 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1784 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1785 # Only local timezone and UTC are supported
1786 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1787 (-_time.timezone, _time.tzname[0])):
1788 if tzseconds < 0:
1789 sign = '-'
1790 seconds = -tzseconds
1791 else:
1792 sign ='+'
1793 seconds = tzseconds
1794 hours, minutes = divmod(seconds//60, 60)
1795 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1796 dt = strptime(dtstr, "%z %Z")
1797 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1798 self.assertEqual(dt.tzname(), tzname)
1799 # Can produce inconsistent datetime
1800 dtstr, fmt = "+1234 UTC", "%z %Z"
1801 dt = strptime(dtstr, fmt)
1802 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1803 self.assertEqual(dt.tzname(), 'UTC')
1804 # yet will roundtrip
1805 self.assertEqual(dt.strftime(fmt), dtstr)
1806
1807 # Produce naive datetime if no %z is provided
1808 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1809
1810 with self.assertRaises(ValueError): strptime("-2400", "%z")
1811 with self.assertRaises(ValueError): strptime("-000", "%z")
1812
Tim Peters2a799bf2002-12-16 20:18:38 +00001813 def test_more_timetuple(self):
1814 # This tests fields beyond those tested by the TestDate.test_timetuple.
1815 t = self.theclass(2004, 12, 31, 6, 22, 33)
1816 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1817 self.assertEqual(t.timetuple(),
1818 (t.year, t.month, t.day,
1819 t.hour, t.minute, t.second,
1820 t.weekday(),
1821 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1822 -1))
1823 tt = t.timetuple()
1824 self.assertEqual(tt.tm_year, t.year)
1825 self.assertEqual(tt.tm_mon, t.month)
1826 self.assertEqual(tt.tm_mday, t.day)
1827 self.assertEqual(tt.tm_hour, t.hour)
1828 self.assertEqual(tt.tm_min, t.minute)
1829 self.assertEqual(tt.tm_sec, t.second)
1830 self.assertEqual(tt.tm_wday, t.weekday())
1831 self.assertEqual(tt.tm_yday, t.toordinal() -
1832 date(t.year, 1, 1).toordinal() + 1)
1833 self.assertEqual(tt.tm_isdst, -1)
1834
1835 def test_more_strftime(self):
1836 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001837 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1838 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1839 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001840
1841 def test_extract(self):
1842 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1843 self.assertEqual(dt.date(), date(2002, 3, 4))
1844 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1845
1846 def test_combine(self):
1847 d = date(2002, 3, 4)
1848 t = time(18, 45, 3, 1234)
1849 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1850 combine = self.theclass.combine
1851 dt = combine(d, t)
1852 self.assertEqual(dt, expected)
1853
1854 dt = combine(time=t, date=d)
1855 self.assertEqual(dt, expected)
1856
1857 self.assertEqual(d, dt.date())
1858 self.assertEqual(t, dt.time())
1859 self.assertEqual(dt, combine(dt.date(), dt.time()))
1860
1861 self.assertRaises(TypeError, combine) # need an arg
1862 self.assertRaises(TypeError, combine, d) # need two args
1863 self.assertRaises(TypeError, combine, t, d) # args reversed
1864 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1865 self.assertRaises(TypeError, combine, "date", "time") # wrong types
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00001866 self.assertRaises(TypeError, combine, d, "time") # wrong type
1867 self.assertRaises(TypeError, combine, "date", t) # wrong type
Tim Peters2a799bf2002-12-16 20:18:38 +00001868
Tim Peters12bf3392002-12-24 05:41:27 +00001869 def test_replace(self):
1870 cls = self.theclass
1871 args = [1, 2, 3, 4, 5, 6, 7]
1872 base = cls(*args)
1873 self.assertEqual(base, base.replace())
1874
1875 i = 0
1876 for name, newval in (("year", 2),
1877 ("month", 3),
1878 ("day", 4),
1879 ("hour", 5),
1880 ("minute", 6),
1881 ("second", 7),
1882 ("microsecond", 8)):
1883 newargs = args[:]
1884 newargs[i] = newval
1885 expected = cls(*newargs)
1886 got = base.replace(**{name: newval})
1887 self.assertEqual(expected, got)
1888 i += 1
1889
1890 # Out of bounds.
1891 base = cls(2000, 2, 29)
1892 self.assertRaises(ValueError, base.replace, year=2001)
1893
Tim Peters80475bb2002-12-25 07:40:55 +00001894 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001895 # Pretty boring! The TZ test is more interesting here. astimezone()
1896 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001897 dt = self.theclass.now()
1898 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001899 self.assertRaises(TypeError, dt.astimezone) # not enough args
1900 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1901 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001902 self.assertRaises(ValueError, dt.astimezone, f) # naive
1903 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001904
Tim Peters52dcce22003-01-23 16:36:11 +00001905 class Bogus(tzinfo):
1906 def utcoffset(self, dt): return None
1907 def dst(self, dt): return timedelta(0)
1908 bog = Bogus()
1909 self.assertRaises(ValueError, dt.astimezone, bog) # naive
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00001910 self.assertRaises(ValueError,
1911 dt.replace(tzinfo=bog).astimezone, f)
Tim Peters52dcce22003-01-23 16:36:11 +00001912
1913 class AlsoBogus(tzinfo):
1914 def utcoffset(self, dt): return timedelta(0)
1915 def dst(self, dt): return None
1916 alsobog = AlsoBogus()
1917 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001918
Tim Petersa98924a2003-05-17 05:55:19 +00001919 def test_subclass_datetime(self):
1920
1921 class C(self.theclass):
1922 theAnswer = 42
1923
1924 def __new__(cls, *args, **kws):
1925 temp = kws.copy()
1926 extra = temp.pop('extra')
1927 result = self.theclass.__new__(cls, *args, **temp)
1928 result.extra = extra
1929 return result
1930
1931 def newmeth(self, start):
1932 return start + self.year + self.month + self.second
1933
1934 args = 2003, 4, 14, 12, 13, 41
1935
1936 dt1 = self.theclass(*args)
1937 dt2 = C(*args, **{'extra': 7})
1938
1939 self.assertEqual(dt2.__class__, C)
1940 self.assertEqual(dt2.theAnswer, 42)
1941 self.assertEqual(dt2.extra, 7)
1942 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1943 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1944 dt1.second - 7)
1945
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001946class TestSubclassDateTime(TestDateTime):
1947 theclass = SubclassDatetime
1948 # Override tests not designed for subclass
1949 def test_roundtrip(self):
1950 pass
1951
Tim Peters604c0132004-06-07 23:04:33 +00001952class SubclassTime(time):
1953 sub_var = 1
1954
Guido van Rossumd8faa362007-04-27 19:54:29 +00001955class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001956
1957 theclass = time
1958
1959 def test_basic_attributes(self):
1960 t = self.theclass(12, 0)
1961 self.assertEqual(t.hour, 12)
1962 self.assertEqual(t.minute, 0)
1963 self.assertEqual(t.second, 0)
1964 self.assertEqual(t.microsecond, 0)
1965
1966 def test_basic_attributes_nonzero(self):
1967 # Make sure all attributes are non-zero so bugs in
1968 # bit-shifting access show up.
1969 t = self.theclass(12, 59, 59, 8000)
1970 self.assertEqual(t.hour, 12)
1971 self.assertEqual(t.minute, 59)
1972 self.assertEqual(t.second, 59)
1973 self.assertEqual(t.microsecond, 8000)
1974
1975 def test_roundtrip(self):
1976 t = self.theclass(1, 2, 3, 4)
1977
1978 # Verify t -> string -> time identity.
1979 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001980 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001981 s = s[9:]
1982 t2 = eval(s)
1983 self.assertEqual(t, t2)
1984
1985 # Verify identity via reconstructing from pieces.
1986 t2 = self.theclass(t.hour, t.minute, t.second,
1987 t.microsecond)
1988 self.assertEqual(t, t2)
1989
1990 def test_comparing(self):
1991 args = [1, 2, 3, 4]
1992 t1 = self.theclass(*args)
1993 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001994 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001995 self.assertTrue(t1 <= t2)
1996 self.assertTrue(t1 >= t2)
1997 self.assertTrue(not t1 != t2)
1998 self.assertTrue(not t1 < t2)
1999 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002000
2001 for i in range(len(args)):
2002 newargs = args[:]
2003 newargs[i] = args[i] + 1
2004 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002005 self.assertTrue(t1 < t2)
2006 self.assertTrue(t2 > t1)
2007 self.assertTrue(t1 <= t2)
2008 self.assertTrue(t2 >= t1)
2009 self.assertTrue(t1 != t2)
2010 self.assertTrue(t2 != t1)
2011 self.assertTrue(not t1 == t2)
2012 self.assertTrue(not t2 == t1)
2013 self.assertTrue(not t1 > t2)
2014 self.assertTrue(not t2 < t1)
2015 self.assertTrue(not t1 >= t2)
2016 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002017
Tim Peters68124bb2003-02-08 03:46:31 +00002018 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00002019 self.assertEqual(t1 == badarg, False)
2020 self.assertEqual(t1 != badarg, True)
2021 self.assertEqual(badarg == t1, False)
2022 self.assertEqual(badarg != t1, True)
2023
Tim Peters2a799bf2002-12-16 20:18:38 +00002024 self.assertRaises(TypeError, lambda: t1 <= badarg)
2025 self.assertRaises(TypeError, lambda: t1 < badarg)
2026 self.assertRaises(TypeError, lambda: t1 > badarg)
2027 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00002028 self.assertRaises(TypeError, lambda: badarg <= t1)
2029 self.assertRaises(TypeError, lambda: badarg < t1)
2030 self.assertRaises(TypeError, lambda: badarg > t1)
2031 self.assertRaises(TypeError, lambda: badarg >= t1)
2032
2033 def test_bad_constructor_arguments(self):
2034 # bad hours
2035 self.theclass(0, 0) # no exception
2036 self.theclass(23, 0) # no exception
2037 self.assertRaises(ValueError, self.theclass, -1, 0)
2038 self.assertRaises(ValueError, self.theclass, 24, 0)
2039 # bad minutes
2040 self.theclass(23, 0) # no exception
2041 self.theclass(23, 59) # no exception
2042 self.assertRaises(ValueError, self.theclass, 23, -1)
2043 self.assertRaises(ValueError, self.theclass, 23, 60)
2044 # bad seconds
2045 self.theclass(23, 59, 0) # no exception
2046 self.theclass(23, 59, 59) # no exception
2047 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2048 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2049 # bad microseconds
2050 self.theclass(23, 59, 59, 0) # no exception
2051 self.theclass(23, 59, 59, 999999) # no exception
2052 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2053 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2054
2055 def test_hash_equality(self):
2056 d = self.theclass(23, 30, 17)
2057 e = self.theclass(23, 30, 17)
2058 self.assertEqual(d, e)
2059 self.assertEqual(hash(d), hash(e))
2060
2061 dic = {d: 1}
2062 dic[e] = 2
2063 self.assertEqual(len(dic), 1)
2064 self.assertEqual(dic[d], 2)
2065 self.assertEqual(dic[e], 2)
2066
2067 d = self.theclass(0, 5, 17)
2068 e = self.theclass(0, 5, 17)
2069 self.assertEqual(d, e)
2070 self.assertEqual(hash(d), hash(e))
2071
2072 dic = {d: 1}
2073 dic[e] = 2
2074 self.assertEqual(len(dic), 1)
2075 self.assertEqual(dic[d], 2)
2076 self.assertEqual(dic[e], 2)
2077
2078 def test_isoformat(self):
2079 t = self.theclass(4, 5, 1, 123)
2080 self.assertEqual(t.isoformat(), "04:05:01.000123")
2081 self.assertEqual(t.isoformat(), str(t))
2082
2083 t = self.theclass()
2084 self.assertEqual(t.isoformat(), "00:00:00")
2085 self.assertEqual(t.isoformat(), str(t))
2086
2087 t = self.theclass(microsecond=1)
2088 self.assertEqual(t.isoformat(), "00:00:00.000001")
2089 self.assertEqual(t.isoformat(), str(t))
2090
2091 t = self.theclass(microsecond=10)
2092 self.assertEqual(t.isoformat(), "00:00:00.000010")
2093 self.assertEqual(t.isoformat(), str(t))
2094
2095 t = self.theclass(microsecond=100)
2096 self.assertEqual(t.isoformat(), "00:00:00.000100")
2097 self.assertEqual(t.isoformat(), str(t))
2098
2099 t = self.theclass(microsecond=1000)
2100 self.assertEqual(t.isoformat(), "00:00:00.001000")
2101 self.assertEqual(t.isoformat(), str(t))
2102
2103 t = self.theclass(microsecond=10000)
2104 self.assertEqual(t.isoformat(), "00:00:00.010000")
2105 self.assertEqual(t.isoformat(), str(t))
2106
2107 t = self.theclass(microsecond=100000)
2108 self.assertEqual(t.isoformat(), "00:00:00.100000")
2109 self.assertEqual(t.isoformat(), str(t))
2110
Thomas Wouterscf297e42007-02-23 15:07:44 +00002111 def test_1653736(self):
2112 # verify it doesn't accept extra keyword arguments
2113 t = self.theclass(second=1)
2114 self.assertRaises(TypeError, t.isoformat, foo=3)
2115
Tim Peters2a799bf2002-12-16 20:18:38 +00002116 def test_strftime(self):
2117 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002118 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002119 # A naive object replaces %z and %Z with empty strings.
2120 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2121
Eric Smith1ba31142007-09-11 18:06:02 +00002122 def test_format(self):
2123 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002124 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002125
2126 # check that a derived class's __str__() gets called
2127 class A(self.theclass):
2128 def __str__(self):
2129 return 'A'
2130 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002131 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002132
2133 # check that a derived class's strftime gets called
2134 class B(self.theclass):
2135 def strftime(self, format_spec):
2136 return 'B'
2137 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002138 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002139
2140 for fmt in ['%H %M %S',
2141 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002142 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2143 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2144 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002145
Tim Peters2a799bf2002-12-16 20:18:38 +00002146 def test_str(self):
2147 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2148 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2149 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2150 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2151 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2152
2153 def test_repr(self):
2154 name = 'datetime.' + self.theclass.__name__
2155 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2156 "%s(1, 2, 3, 4)" % name)
2157 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2158 "%s(10, 2, 3, 4000)" % name)
2159 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2160 "%s(0, 2, 3, 400000)" % name)
2161 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2162 "%s(12, 2, 3)" % name)
2163 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2164 "%s(23, 15)" % name)
2165
2166 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002167 self.assertIsInstance(self.theclass.min, self.theclass)
2168 self.assertIsInstance(self.theclass.max, self.theclass)
2169 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002170 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002171
2172 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002173 args = 20, 59, 16, 64**2
2174 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002175 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002176 green = pickler.dumps(orig, proto)
2177 derived = unpickler.loads(green)
2178 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002179
Tim Peters604c0132004-06-07 23:04:33 +00002180 def test_pickling_subclass_time(self):
2181 args = 20, 59, 16, 64**2
2182 orig = SubclassTime(*args)
2183 for pickler, unpickler, proto in pickle_choices:
2184 green = pickler.dumps(orig, proto)
2185 derived = unpickler.loads(green)
2186 self.assertEqual(orig, derived)
2187
Tim Peters2a799bf2002-12-16 20:18:38 +00002188 def test_bool(self):
2189 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002190 self.assertTrue(cls(1))
2191 self.assertTrue(cls(0, 1))
2192 self.assertTrue(cls(0, 0, 1))
2193 self.assertTrue(cls(0, 0, 0, 1))
2194 self.assertTrue(not cls(0))
2195 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002196
Tim Peters12bf3392002-12-24 05:41:27 +00002197 def test_replace(self):
2198 cls = self.theclass
2199 args = [1, 2, 3, 4]
2200 base = cls(*args)
2201 self.assertEqual(base, base.replace())
2202
2203 i = 0
2204 for name, newval in (("hour", 5),
2205 ("minute", 6),
2206 ("second", 7),
2207 ("microsecond", 8)):
2208 newargs = args[:]
2209 newargs[i] = newval
2210 expected = cls(*newargs)
2211 got = base.replace(**{name: newval})
2212 self.assertEqual(expected, got)
2213 i += 1
2214
2215 # Out of bounds.
2216 base = cls(1)
2217 self.assertRaises(ValueError, base.replace, hour=24)
2218 self.assertRaises(ValueError, base.replace, minute=-1)
2219 self.assertRaises(ValueError, base.replace, second=100)
2220 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2221
Tim Petersa98924a2003-05-17 05:55:19 +00002222 def test_subclass_time(self):
2223
2224 class C(self.theclass):
2225 theAnswer = 42
2226
2227 def __new__(cls, *args, **kws):
2228 temp = kws.copy()
2229 extra = temp.pop('extra')
2230 result = self.theclass.__new__(cls, *args, **temp)
2231 result.extra = extra
2232 return result
2233
2234 def newmeth(self, start):
2235 return start + self.hour + self.second
2236
2237 args = 4, 5, 6
2238
2239 dt1 = self.theclass(*args)
2240 dt2 = C(*args, **{'extra': 7})
2241
2242 self.assertEqual(dt2.__class__, C)
2243 self.assertEqual(dt2.theAnswer, 42)
2244 self.assertEqual(dt2.extra, 7)
2245 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2246 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2247
Armin Rigof4afb212005-11-07 07:15:48 +00002248 def test_backdoor_resistance(self):
2249 # see TestDate.test_backdoor_resistance().
2250 base = '2:59.0'
2251 for hour_byte in ' ', '9', chr(24), '\xff':
2252 self.assertRaises(TypeError, self.theclass,
2253 hour_byte + base[1:])
2254
Tim Peters855fe882002-12-22 03:43:39 +00002255# A mixin for classes with a tzinfo= argument. Subclasses must define
2256# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002257# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002258class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002259
Tim Petersbad8ff02002-12-30 20:52:32 +00002260 def test_argument_passing(self):
2261 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002262 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002263 class introspective(tzinfo):
2264 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002265 def utcoffset(self, dt):
2266 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002267 dst = utcoffset
2268
2269 obj = cls(1, 2, 3, tzinfo=introspective())
2270
Tim Peters0bf60bd2003-01-08 20:40:01 +00002271 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002272 self.assertEqual(obj.tzname(), expected)
2273
Tim Peters0bf60bd2003-01-08 20:40:01 +00002274 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002275 self.assertEqual(obj.utcoffset(), expected)
2276 self.assertEqual(obj.dst(), expected)
2277
Tim Peters855fe882002-12-22 03:43:39 +00002278 def test_bad_tzinfo_classes(self):
2279 cls = self.theclass
2280 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002281
Tim Peters855fe882002-12-22 03:43:39 +00002282 class NiceTry(object):
2283 def __init__(self): pass
2284 def utcoffset(self, dt): pass
2285 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2286
2287 class BetterTry(tzinfo):
2288 def __init__(self): pass
2289 def utcoffset(self, dt): pass
2290 b = BetterTry()
2291 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002292 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002293
2294 def test_utc_offset_out_of_bounds(self):
2295 class Edgy(tzinfo):
2296 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002297 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002298 def utcoffset(self, dt):
2299 return self.offset
2300
2301 cls = self.theclass
2302 for offset, legit in ((-1440, False),
2303 (-1439, True),
2304 (1439, True),
2305 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002306 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002307 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002308 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002309 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002310 else:
2311 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002312 if legit:
2313 aofs = abs(offset)
2314 h, m = divmod(aofs, 60)
2315 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002316 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002317 t = t.timetz()
2318 self.assertEqual(str(t), "01:02:03" + tag)
2319 else:
2320 self.assertRaises(ValueError, str, t)
2321
2322 def test_tzinfo_classes(self):
2323 cls = self.theclass
2324 class C1(tzinfo):
2325 def utcoffset(self, dt): return None
2326 def dst(self, dt): return None
2327 def tzname(self, dt): return None
2328 for t in (cls(1, 1, 1),
2329 cls(1, 1, 1, tzinfo=None),
2330 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002331 self.assertTrue(t.utcoffset() is None)
2332 self.assertTrue(t.dst() is None)
2333 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002334
Tim Peters855fe882002-12-22 03:43:39 +00002335 class C3(tzinfo):
2336 def utcoffset(self, dt): return timedelta(minutes=-1439)
2337 def dst(self, dt): return timedelta(minutes=1439)
2338 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002339 t = cls(1, 1, 1, tzinfo=C3())
2340 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2341 self.assertEqual(t.dst(), timedelta(minutes=1439))
2342 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002343
2344 # Wrong types.
2345 class C4(tzinfo):
2346 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002347 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002348 def tzname(self, dt): return 0
2349 t = cls(1, 1, 1, tzinfo=C4())
2350 self.assertRaises(TypeError, t.utcoffset)
2351 self.assertRaises(TypeError, t.dst)
2352 self.assertRaises(TypeError, t.tzname)
2353
2354 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002355 class C6(tzinfo):
2356 def utcoffset(self, dt): return timedelta(hours=-24)
2357 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002358 t = cls(1, 1, 1, tzinfo=C6())
2359 self.assertRaises(ValueError, t.utcoffset)
2360 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002361
2362 # Not a whole number of minutes.
2363 class C7(tzinfo):
2364 def utcoffset(self, dt): return timedelta(seconds=61)
2365 def dst(self, dt): return timedelta(microseconds=-81)
2366 t = cls(1, 1, 1, tzinfo=C7())
2367 self.assertRaises(ValueError, t.utcoffset)
2368 self.assertRaises(ValueError, t.dst)
2369
Tim Peters4c0db782002-12-26 05:01:19 +00002370 def test_aware_compare(self):
2371 cls = self.theclass
2372
Tim Peters60c76e42002-12-27 00:41:11 +00002373 # Ensure that utcoffset() gets ignored if the comparands have
2374 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002375 class OperandDependentOffset(tzinfo):
2376 def utcoffset(self, t):
2377 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002378 # d0 and d1 equal after adjustment
2379 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002380 else:
Tim Peters397301e2003-01-02 21:28:08 +00002381 # d2 off in the weeds
2382 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002383
2384 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2385 d0 = base.replace(minute=3)
2386 d1 = base.replace(minute=9)
2387 d2 = base.replace(minute=11)
2388 for x in d0, d1, d2:
2389 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002390 for op in lt, le, gt, ge, eq, ne:
2391 got = op(x, y)
2392 expected = op(x.minute, y.minute)
2393 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002394
2395 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002396 # Note that a time can't actually have an operand-depedent offset,
2397 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2398 # so skip this test for time.
2399 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002400 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2401 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2402 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2403 for x in d0, d1, d2:
2404 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002405 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002406 if (x is d0 or x is d1) and (y is d0 or y is d1):
2407 expected = 0
2408 elif x is y is d2:
2409 expected = 0
2410 elif x is d2:
2411 expected = -1
2412 else:
2413 assert y is d2
2414 expected = 1
2415 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002416
Tim Peters855fe882002-12-22 03:43:39 +00002417
Tim Peters0bf60bd2003-01-08 20:40:01 +00002418# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002419class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002420 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002421
2422 def test_empty(self):
2423 t = self.theclass()
2424 self.assertEqual(t.hour, 0)
2425 self.assertEqual(t.minute, 0)
2426 self.assertEqual(t.second, 0)
2427 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002428 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002429
Tim Peters2a799bf2002-12-16 20:18:38 +00002430 def test_zones(self):
2431 est = FixedOffset(-300, "EST", 1)
2432 utc = FixedOffset(0, "UTC", -2)
2433 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002434 t1 = time( 7, 47, tzinfo=est)
2435 t2 = time(12, 47, tzinfo=utc)
2436 t3 = time(13, 47, tzinfo=met)
2437 t4 = time(microsecond=40)
2438 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002439
2440 self.assertEqual(t1.tzinfo, est)
2441 self.assertEqual(t2.tzinfo, utc)
2442 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002443 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002444 self.assertEqual(t5.tzinfo, utc)
2445
Tim Peters855fe882002-12-22 03:43:39 +00002446 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2447 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2448 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002449 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002450 self.assertRaises(TypeError, t1.utcoffset, "no args")
2451
2452 self.assertEqual(t1.tzname(), "EST")
2453 self.assertEqual(t2.tzname(), "UTC")
2454 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002455 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002456 self.assertRaises(TypeError, t1.tzname, "no args")
2457
Tim Peters855fe882002-12-22 03:43:39 +00002458 self.assertEqual(t1.dst(), timedelta(minutes=1))
2459 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2460 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002461 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002462 self.assertRaises(TypeError, t1.dst, "no args")
2463
2464 self.assertEqual(hash(t1), hash(t2))
2465 self.assertEqual(hash(t1), hash(t3))
2466 self.assertEqual(hash(t2), hash(t3))
2467
2468 self.assertEqual(t1, t2)
2469 self.assertEqual(t1, t3)
2470 self.assertEqual(t2, t3)
2471 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2472 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2473 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2474
2475 self.assertEqual(str(t1), "07:47:00-05:00")
2476 self.assertEqual(str(t2), "12:47:00+00:00")
2477 self.assertEqual(str(t3), "13:47:00+01:00")
2478 self.assertEqual(str(t4), "00:00:00.000040")
2479 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2480
2481 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2482 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2483 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2484 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2485 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2486
Tim Peters0bf60bd2003-01-08 20:40:01 +00002487 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002488 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2489 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2490 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2491 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2492 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2493
2494 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2495 "07:47:00 %Z=EST %z=-0500")
2496 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2497 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2498
2499 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002500 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002501 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2502 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2503
Tim Petersb92bb712002-12-21 17:44:07 +00002504 # Check that an invalid tzname result raises an exception.
2505 class Badtzname(tzinfo):
2506 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002507 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002508 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2509 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002510
2511 def test_hash_edge_cases(self):
2512 # Offsets that overflow a basic time.
2513 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2514 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2515 self.assertEqual(hash(t1), hash(t2))
2516
2517 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2518 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2519 self.assertEqual(hash(t1), hash(t2))
2520
Tim Peters2a799bf2002-12-16 20:18:38 +00002521 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002522 # Try one without a tzinfo.
2523 args = 20, 59, 16, 64**2
2524 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002525 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002526 green = pickler.dumps(orig, proto)
2527 derived = unpickler.loads(green)
2528 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002529
2530 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002531 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002532 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002533 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002534 green = pickler.dumps(orig, proto)
2535 derived = unpickler.loads(green)
2536 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002537 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002538 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2539 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002540
2541 def test_more_bool(self):
2542 # Test cases with non-None tzinfo.
2543 cls = self.theclass
2544
2545 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002546 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002547
2548 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002549 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002550
2551 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002552 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002553
2554 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002555 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002556
2557 # Mostly ensuring this doesn't overflow internally.
2558 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002559 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002560
2561 # But this should yield a value error -- the utcoffset is bogus.
2562 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2563 self.assertRaises(ValueError, lambda: bool(t))
2564
2565 # Likewise.
2566 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2567 self.assertRaises(ValueError, lambda: bool(t))
2568
Tim Peters12bf3392002-12-24 05:41:27 +00002569 def test_replace(self):
2570 cls = self.theclass
2571 z100 = FixedOffset(100, "+100")
2572 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2573 args = [1, 2, 3, 4, z100]
2574 base = cls(*args)
2575 self.assertEqual(base, base.replace())
2576
2577 i = 0
2578 for name, newval in (("hour", 5),
2579 ("minute", 6),
2580 ("second", 7),
2581 ("microsecond", 8),
2582 ("tzinfo", zm200)):
2583 newargs = args[:]
2584 newargs[i] = newval
2585 expected = cls(*newargs)
2586 got = base.replace(**{name: newval})
2587 self.assertEqual(expected, got)
2588 i += 1
2589
2590 # Ensure we can get rid of a tzinfo.
2591 self.assertEqual(base.tzname(), "+100")
2592 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002593 self.assertTrue(base2.tzinfo is None)
2594 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002595
2596 # Ensure we can add one.
2597 base3 = base2.replace(tzinfo=z100)
2598 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002599 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002600
2601 # Out of bounds.
2602 base = cls(1)
2603 self.assertRaises(ValueError, base.replace, hour=24)
2604 self.assertRaises(ValueError, base.replace, minute=-1)
2605 self.assertRaises(ValueError, base.replace, second=100)
2606 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2607
Tim Peters60c76e42002-12-27 00:41:11 +00002608 def test_mixed_compare(self):
2609 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002610 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002611 self.assertEqual(t1, t2)
2612 t2 = t2.replace(tzinfo=None)
2613 self.assertEqual(t1, t2)
2614 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2615 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002616 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2617 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002618
Tim Peters0bf60bd2003-01-08 20:40:01 +00002619 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002620 class Varies(tzinfo):
2621 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002622 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002623 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002624 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002625 return self.offset
2626
2627 v = Varies()
2628 t1 = t2.replace(tzinfo=v)
2629 t2 = t2.replace(tzinfo=v)
2630 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2631 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2632 self.assertEqual(t1, t2)
2633
2634 # But if they're not identical, it isn't ignored.
2635 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002636 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002637
Tim Petersa98924a2003-05-17 05:55:19 +00002638 def test_subclass_timetz(self):
2639
2640 class C(self.theclass):
2641 theAnswer = 42
2642
2643 def __new__(cls, *args, **kws):
2644 temp = kws.copy()
2645 extra = temp.pop('extra')
2646 result = self.theclass.__new__(cls, *args, **temp)
2647 result.extra = extra
2648 return result
2649
2650 def newmeth(self, start):
2651 return start + self.hour + self.second
2652
2653 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2654
2655 dt1 = self.theclass(*args)
2656 dt2 = C(*args, **{'extra': 7})
2657
2658 self.assertEqual(dt2.__class__, C)
2659 self.assertEqual(dt2.theAnswer, 42)
2660 self.assertEqual(dt2.extra, 7)
2661 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2662 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2663
Tim Peters4c0db782002-12-26 05:01:19 +00002664
Tim Peters0bf60bd2003-01-08 20:40:01 +00002665# Testing datetime objects with a non-None tzinfo.
2666
Guido van Rossumd8faa362007-04-27 19:54:29 +00002667class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002668 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002669
2670 def test_trivial(self):
2671 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2672 self.assertEqual(dt.year, 1)
2673 self.assertEqual(dt.month, 2)
2674 self.assertEqual(dt.day, 3)
2675 self.assertEqual(dt.hour, 4)
2676 self.assertEqual(dt.minute, 5)
2677 self.assertEqual(dt.second, 6)
2678 self.assertEqual(dt.microsecond, 7)
2679 self.assertEqual(dt.tzinfo, None)
2680
2681 def test_even_more_compare(self):
2682 # The test_compare() and test_more_compare() inherited from TestDate
2683 # and TestDateTime covered non-tzinfo cases.
2684
2685 # Smallest possible after UTC adjustment.
2686 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2687 # Largest possible after UTC adjustment.
2688 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2689 tzinfo=FixedOffset(-1439, ""))
2690
2691 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002692 self.assertTrue(t1 < t2)
2693 self.assertTrue(t1 != t2)
2694 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002695
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002696 self.assertEqual(t1, t1)
2697 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002698
2699 # Equal afer adjustment.
2700 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2701 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2702 self.assertEqual(t1, t2)
2703
2704 # Change t1 not to subtract a minute, and t1 should be larger.
2705 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002706 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002707
2708 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2709 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002710 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002711
2712 # Back to the original t1, but make seconds resolve it.
2713 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2714 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002715 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002716
2717 # Likewise, but make microseconds resolve it.
2718 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2719 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002720 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002721
2722 # Make t2 naive and it should fail.
2723 t2 = self.theclass.min
2724 self.assertRaises(TypeError, lambda: t1 == t2)
2725 self.assertEqual(t2, t2)
2726
2727 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2728 class Naive(tzinfo):
2729 def utcoffset(self, dt): return None
2730 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2731 self.assertRaises(TypeError, lambda: t1 == t2)
2732 self.assertEqual(t2, t2)
2733
2734 # OTOH, it's OK to compare two of these mixing the two ways of being
2735 # naive.
2736 t1 = self.theclass(5, 6, 7)
2737 self.assertEqual(t1, t2)
2738
2739 # Try a bogus uctoffset.
2740 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002741 def utcoffset(self, dt):
2742 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002743 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2744 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002745 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002746
Tim Peters2a799bf2002-12-16 20:18:38 +00002747 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002748 # Try one without a tzinfo.
2749 args = 6, 7, 23, 20, 59, 1, 64**2
2750 orig = self.theclass(*args)
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)
Tim Peters2a799bf2002-12-16 20:18:38 +00002755
2756 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002757 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002758 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002759 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002760 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002761 green = pickler.dumps(orig, proto)
2762 derived = unpickler.loads(green)
2763 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002764 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002765 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2766 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002767
2768 def test_extreme_hashes(self):
2769 # If an attempt is made to hash these via subtracting the offset
2770 # then hashing a datetime object, OverflowError results. The
2771 # Python implementation used to blow up here.
2772 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2773 hash(t)
2774 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2775 tzinfo=FixedOffset(-1439, ""))
2776 hash(t)
2777
2778 # OTOH, an OOB offset should blow up.
2779 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2780 self.assertRaises(ValueError, hash, t)
2781
2782 def test_zones(self):
2783 est = FixedOffset(-300, "EST")
2784 utc = FixedOffset(0, "UTC")
2785 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002786 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2787 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2788 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002789 self.assertEqual(t1.tzinfo, est)
2790 self.assertEqual(t2.tzinfo, utc)
2791 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002792 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2793 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2794 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002795 self.assertEqual(t1.tzname(), "EST")
2796 self.assertEqual(t2.tzname(), "UTC")
2797 self.assertEqual(t3.tzname(), "MET")
2798 self.assertEqual(hash(t1), hash(t2))
2799 self.assertEqual(hash(t1), hash(t3))
2800 self.assertEqual(hash(t2), hash(t3))
2801 self.assertEqual(t1, t2)
2802 self.assertEqual(t1, t3)
2803 self.assertEqual(t2, t3)
2804 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2805 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2806 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002807 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002808 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2809 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2810 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2811
2812 def test_combine(self):
2813 met = FixedOffset(60, "MET")
2814 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002815 tz = time(18, 45, 3, 1234, tzinfo=met)
2816 dt = datetime.combine(d, tz)
2817 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002818 tzinfo=met))
2819
2820 def test_extract(self):
2821 met = FixedOffset(60, "MET")
2822 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2823 self.assertEqual(dt.date(), date(2002, 3, 4))
2824 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002825 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002826
2827 def test_tz_aware_arithmetic(self):
2828 import random
2829
2830 now = self.theclass.now()
2831 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002832 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002833 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002834 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002835 self.assertEqual(nowaware.timetz(), timeaware)
2836
2837 # Can't mix aware and non-aware.
2838 self.assertRaises(TypeError, lambda: now - nowaware)
2839 self.assertRaises(TypeError, lambda: nowaware - now)
2840
Tim Peters0bf60bd2003-01-08 20:40:01 +00002841 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002842 self.assertRaises(TypeError, lambda: now + nowaware)
2843 self.assertRaises(TypeError, lambda: nowaware + now)
2844 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2845
2846 # Subtracting should yield 0.
2847 self.assertEqual(now - now, timedelta(0))
2848 self.assertEqual(nowaware - nowaware, timedelta(0))
2849
2850 # Adding a delta should preserve tzinfo.
2851 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2852 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002853 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002854 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002855 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002856 self.assertEqual(nowawareplus, nowawareplus2)
2857
2858 # that - delta should be what we started with, and that - what we
2859 # started with should be delta.
2860 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002861 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002862 self.assertEqual(nowaware, diff)
2863 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2864 self.assertEqual(nowawareplus - nowaware, delta)
2865
2866 # Make up a random timezone.
2867 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002868 # Attach it to nowawareplus.
2869 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002870 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002871 # Make sure the difference takes the timezone adjustments into account.
2872 got = nowaware - nowawareplus
2873 # Expected: (nowaware base - nowaware offset) -
2874 # (nowawareplus base - nowawareplus offset) =
2875 # (nowaware base - nowawareplus base) +
2876 # (nowawareplus offset - nowaware offset) =
2877 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002878 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002879 self.assertEqual(got, expected)
2880
2881 # Try max possible difference.
2882 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2883 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2884 tzinfo=FixedOffset(-1439, "max"))
2885 maxdiff = max - min
2886 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2887 timedelta(minutes=2*1439))
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00002888 # Different tzinfo, but the same offset
2889 tza = timezone(HOUR, 'A')
2890 tzb = timezone(HOUR, 'B')
2891 delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
2892 self.assertEqual(delta, self.theclass.min - self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00002893
2894 def test_tzinfo_now(self):
2895 meth = self.theclass.now
2896 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2897 base = meth()
2898 # Try with and without naming the keyword.
2899 off42 = FixedOffset(42, "42")
2900 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002901 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002902 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002903 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002904 # Bad argument with and w/o naming the keyword.
2905 self.assertRaises(TypeError, meth, 16)
2906 self.assertRaises(TypeError, meth, tzinfo=16)
2907 # Bad keyword name.
2908 self.assertRaises(TypeError, meth, tinfo=off42)
2909 # Too many args.
2910 self.assertRaises(TypeError, meth, off42, off42)
2911
Tim Peters10cadce2003-01-23 19:58:02 +00002912 # We don't know which time zone we're in, and don't have a tzinfo
2913 # class to represent it, so seeing whether a tz argument actually
2914 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002915 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002916 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2917 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2918 for dummy in range(3):
2919 now = datetime.now(weirdtz)
2920 self.assertTrue(now.tzinfo is weirdtz)
2921 utcnow = datetime.utcnow().replace(tzinfo=utc)
2922 now2 = utcnow.astimezone(weirdtz)
2923 if abs(now - now2) < timedelta(seconds=30):
2924 break
2925 # Else the code is broken, or more than 30 seconds passed between
2926 # calls; assuming the latter, just try again.
2927 else:
2928 # Three strikes and we're out.
2929 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002930
Tim Peters2a799bf2002-12-16 20:18:38 +00002931 def test_tzinfo_fromtimestamp(self):
2932 import time
2933 meth = self.theclass.fromtimestamp
2934 ts = time.time()
2935 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2936 base = meth(ts)
2937 # Try with and without naming the keyword.
2938 off42 = FixedOffset(42, "42")
2939 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002940 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002941 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002942 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002943 # Bad argument with and w/o naming the keyword.
2944 self.assertRaises(TypeError, meth, ts, 16)
2945 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2946 # Bad keyword name.
2947 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2948 # Too many args.
2949 self.assertRaises(TypeError, meth, ts, off42, off42)
2950 # Too few args.
2951 self.assertRaises(TypeError, meth)
2952
Tim Peters2a44a8d2003-01-23 20:53:10 +00002953 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002954 timestamp = 1000000000
2955 utcdatetime = datetime.utcfromtimestamp(timestamp)
2956 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2957 # But on some flavor of Mac, it's nowhere near that. So we can't have
2958 # any idea here what time that actually is, we can only test that
2959 # relative changes match.
2960 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2961 tz = FixedOffset(utcoffset, "tz", 0)
2962 expected = utcdatetime + utcoffset
2963 got = datetime.fromtimestamp(timestamp, tz)
2964 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002965
Tim Peters2a799bf2002-12-16 20:18:38 +00002966 def test_tzinfo_utcnow(self):
2967 meth = self.theclass.utcnow
2968 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2969 base = meth()
2970 # Try with and without naming the keyword; for whatever reason,
2971 # utcnow() doesn't accept a tzinfo argument.
2972 off42 = FixedOffset(42, "42")
2973 self.assertRaises(TypeError, meth, off42)
2974 self.assertRaises(TypeError, meth, tzinfo=off42)
2975
2976 def test_tzinfo_utcfromtimestamp(self):
2977 import time
2978 meth = self.theclass.utcfromtimestamp
2979 ts = time.time()
2980 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2981 base = meth(ts)
2982 # Try with and without naming the keyword; for whatever reason,
2983 # utcfromtimestamp() doesn't accept a tzinfo argument.
2984 off42 = FixedOffset(42, "42")
2985 self.assertRaises(TypeError, meth, ts, off42)
2986 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2987
2988 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002989 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002990 # DST flag.
2991 class DST(tzinfo):
2992 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002993 if isinstance(dstvalue, int):
2994 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002995 self.dstvalue = dstvalue
2996 def dst(self, dt):
2997 return self.dstvalue
2998
2999 cls = self.theclass
3000 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
3001 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
3002 t = d.timetuple()
3003 self.assertEqual(1, t.tm_year)
3004 self.assertEqual(1, t.tm_mon)
3005 self.assertEqual(1, t.tm_mday)
3006 self.assertEqual(10, t.tm_hour)
3007 self.assertEqual(20, t.tm_min)
3008 self.assertEqual(30, t.tm_sec)
3009 self.assertEqual(0, t.tm_wday)
3010 self.assertEqual(1, t.tm_yday)
3011 self.assertEqual(flag, t.tm_isdst)
3012
3013 # dst() returns wrong type.
3014 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
3015
3016 # dst() at the edge.
3017 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
3018 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
3019
3020 # dst() out of range.
3021 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
3022 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
3023
3024 def test_utctimetuple(self):
3025 class DST(tzinfo):
Alexander Belopolskyf34e82e2010-07-05 15:05:33 +00003026 def __init__(self, dstvalue=0):
Tim Peters397301e2003-01-02 21:28:08 +00003027 if isinstance(dstvalue, int):
3028 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00003029 self.dstvalue = dstvalue
3030 def dst(self, dt):
3031 return self.dstvalue
3032
3033 cls = self.theclass
3034 # This can't work: DST didn't implement utcoffset.
3035 self.assertRaises(NotImplementedError,
3036 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3037
3038 class UOFS(DST):
3039 def __init__(self, uofs, dofs=None):
3040 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00003041 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00003042 def utcoffset(self, dt):
3043 return self.uofs
3044
Tim Peters2a799bf2002-12-16 20:18:38 +00003045 for dstvalue in -33, 33, 0, None:
3046 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3047 t = d.utctimetuple()
3048 self.assertEqual(d.year, t.tm_year)
3049 self.assertEqual(d.month, t.tm_mon)
3050 self.assertEqual(d.day, t.tm_mday)
3051 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3052 self.assertEqual(13, t.tm_min)
3053 self.assertEqual(d.second, t.tm_sec)
3054 self.assertEqual(d.weekday(), t.tm_wday)
3055 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3056 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003057 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3058 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003059 self.assertEqual(0, t.tm_isdst)
3060
Alexander Belopolskyf34e82e2010-07-05 15:05:33 +00003061 # For naive datetime, utctimetuple == timetuple except for isdst
3062 d = cls(1, 2, 3, 10, 20, 30, 40)
3063 t = d.utctimetuple()
3064 self.assertEqual(t[:-1], d.timetuple()[:-1])
3065 self.assertEqual(0, t.tm_isdst)
3066 # Same if utcoffset is None
3067 class NOFS(DST):
3068 def utcoffset(self, dt):
3069 return None
3070 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
3071 t = d.utctimetuple()
3072 self.assertEqual(t[:-1], d.timetuple()[:-1])
3073 self.assertEqual(0, t.tm_isdst)
3074 # Check that bad tzinfo is detected
3075 class BOFS(DST):
3076 def utcoffset(self, dt):
3077 return "EST"
3078 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
3079 self.assertRaises(TypeError, d.utctimetuple)
3080
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003081 # Check that utctimetuple() is the same as
3082 # astimezone(utc).timetuple()
3083 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3084 for tz in [timezone.min, timezone.utc, timezone.max]:
3085 dtz = d.replace(tzinfo=tz)
3086 self.assertEqual(dtz.utctimetuple()[:-1],
3087 dtz.astimezone(timezone.utc).timetuple()[:-1])
3088 # At the edges, UTC adjustment can produce years out-of-range
3089 # for a datetime object. Ensure that an OverflowError is
3090 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003091 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3092 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003093 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003094
3095 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3096 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003097 self.assertRaises(OverflowError, huge.utctimetuple)
3098 # More overflow cases
3099 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3100 self.assertRaises(OverflowError, tiny.utctimetuple)
3101 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3102 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003103
3104 def test_tzinfo_isoformat(self):
3105 zero = FixedOffset(0, "+00:00")
3106 plus = FixedOffset(220, "+03:40")
3107 minus = FixedOffset(-231, "-03:51")
3108 unknown = FixedOffset(None, "")
3109
3110 cls = self.theclass
3111 datestr = '0001-02-03'
3112 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003113 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003114 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3115 timestr = '04:05:59' + (us and '.987001' or '')
3116 ofsstr = ofs is not None and d.tzname() or ''
3117 tailstr = timestr + ofsstr
3118 iso = d.isoformat()
3119 self.assertEqual(iso, datestr + 'T' + tailstr)
3120 self.assertEqual(iso, d.isoformat('T'))
3121 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003122 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003123 self.assertEqual(str(d), datestr + ' ' + tailstr)
3124
Tim Peters12bf3392002-12-24 05:41:27 +00003125 def test_replace(self):
3126 cls = self.theclass
3127 z100 = FixedOffset(100, "+100")
3128 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3129 args = [1, 2, 3, 4, 5, 6, 7, z100]
3130 base = cls(*args)
3131 self.assertEqual(base, base.replace())
3132
3133 i = 0
3134 for name, newval in (("year", 2),
3135 ("month", 3),
3136 ("day", 4),
3137 ("hour", 5),
3138 ("minute", 6),
3139 ("second", 7),
3140 ("microsecond", 8),
3141 ("tzinfo", zm200)):
3142 newargs = args[:]
3143 newargs[i] = newval
3144 expected = cls(*newargs)
3145 got = base.replace(**{name: newval})
3146 self.assertEqual(expected, got)
3147 i += 1
3148
3149 # Ensure we can get rid of a tzinfo.
3150 self.assertEqual(base.tzname(), "+100")
3151 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003152 self.assertTrue(base2.tzinfo is None)
3153 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003154
3155 # Ensure we can add one.
3156 base3 = base2.replace(tzinfo=z100)
3157 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003158 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003159
3160 # Out of bounds.
3161 base = cls(2000, 2, 29)
3162 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003163
Tim Peters80475bb2002-12-25 07:40:55 +00003164 def test_more_astimezone(self):
3165 # The inherited test_astimezone covered some trivial and error cases.
3166 fnone = FixedOffset(None, "None")
3167 f44m = FixedOffset(44, "44")
3168 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3169
Tim Peters10cadce2003-01-23 19:58:02 +00003170 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003171 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003172 # Replacing with degenerate tzinfo raises an exception.
3173 self.assertRaises(ValueError, dt.astimezone, fnone)
3174 # Ditto with None tz.
3175 self.assertRaises(TypeError, dt.astimezone, None)
3176 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003177 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003178 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003179 self.assertEqual(x.date(), dt.date())
3180 self.assertEqual(x.time(), dt.time())
3181
3182 # Replacing with different tzinfo does adjust.
3183 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003184 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003185 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3186 expected = dt - dt.utcoffset() # in effect, convert to UTC
3187 expected += fm5h.utcoffset(dt) # and from there to local time
3188 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3189 self.assertEqual(got.date(), expected.date())
3190 self.assertEqual(got.time(), expected.time())
3191 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003192 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003193 self.assertEqual(got, expected)
3194
Tim Peters4c0db782002-12-26 05:01:19 +00003195 def test_aware_subtract(self):
3196 cls = self.theclass
3197
Tim Peters60c76e42002-12-27 00:41:11 +00003198 # Ensure that utcoffset() is ignored when the operands have the
3199 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003200 class OperandDependentOffset(tzinfo):
3201 def utcoffset(self, t):
3202 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003203 # d0 and d1 equal after adjustment
3204 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003205 else:
Tim Peters397301e2003-01-02 21:28:08 +00003206 # d2 off in the weeds
3207 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003208
3209 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3210 d0 = base.replace(minute=3)
3211 d1 = base.replace(minute=9)
3212 d2 = base.replace(minute=11)
3213 for x in d0, d1, d2:
3214 for y in d0, d1, d2:
3215 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003216 expected = timedelta(minutes=x.minute - y.minute)
3217 self.assertEqual(got, expected)
3218
3219 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3220 # ignored.
3221 base = cls(8, 9, 10, 11, 12, 13, 14)
3222 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3223 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3224 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3225 for x in d0, d1, d2:
3226 for y in d0, d1, d2:
3227 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003228 if (x is d0 or x is d1) and (y is d0 or y is d1):
3229 expected = timedelta(0)
3230 elif x is y is d2:
3231 expected = timedelta(0)
3232 elif x is d2:
3233 expected = timedelta(minutes=(11-59)-0)
3234 else:
3235 assert y is d2
3236 expected = timedelta(minutes=0-(11-59))
3237 self.assertEqual(got, expected)
3238
Tim Peters60c76e42002-12-27 00:41:11 +00003239 def test_mixed_compare(self):
3240 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003241 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003242 self.assertEqual(t1, t2)
3243 t2 = t2.replace(tzinfo=None)
3244 self.assertEqual(t1, t2)
3245 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3246 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003247 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3248 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003249
Tim Peters0bf60bd2003-01-08 20:40:01 +00003250 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003251 class Varies(tzinfo):
3252 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003253 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003254 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003255 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003256 return self.offset
3257
3258 v = Varies()
3259 t1 = t2.replace(tzinfo=v)
3260 t2 = t2.replace(tzinfo=v)
3261 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3262 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3263 self.assertEqual(t1, t2)
3264
3265 # But if they're not identical, it isn't ignored.
3266 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003267 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003268
Tim Petersa98924a2003-05-17 05:55:19 +00003269 def test_subclass_datetimetz(self):
3270
3271 class C(self.theclass):
3272 theAnswer = 42
3273
3274 def __new__(cls, *args, **kws):
3275 temp = kws.copy()
3276 extra = temp.pop('extra')
3277 result = self.theclass.__new__(cls, *args, **temp)
3278 result.extra = extra
3279 return result
3280
3281 def newmeth(self, start):
3282 return start + self.hour + self.year
3283
3284 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3285
3286 dt1 = self.theclass(*args)
3287 dt2 = C(*args, **{'extra': 7})
3288
3289 self.assertEqual(dt2.__class__, C)
3290 self.assertEqual(dt2.theAnswer, 42)
3291 self.assertEqual(dt2.extra, 7)
3292 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3293 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3294
Tim Peters621818b2002-12-29 23:44:49 +00003295# Pain to set up DST-aware tzinfo classes.
3296
3297def first_sunday_on_or_after(dt):
3298 days_to_go = 6 - dt.weekday()
3299 if days_to_go:
3300 dt += timedelta(days_to_go)
3301 return dt
3302
3303ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003304MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003305HOUR = timedelta(hours=1)
3306DAY = timedelta(days=1)
3307# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3308DSTSTART = datetime(1, 4, 1, 2)
3309# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003310# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3311# being standard time on that day, there is no spelling in local time of
3312# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3313DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003314
3315class USTimeZone(tzinfo):
3316
3317 def __init__(self, hours, reprname, stdname, dstname):
3318 self.stdoffset = timedelta(hours=hours)
3319 self.reprname = reprname
3320 self.stdname = stdname
3321 self.dstname = dstname
3322
3323 def __repr__(self):
3324 return self.reprname
3325
3326 def tzname(self, dt):
3327 if self.dst(dt):
3328 return self.dstname
3329 else:
3330 return self.stdname
3331
3332 def utcoffset(self, dt):
3333 return self.stdoffset + self.dst(dt)
3334
3335 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003336 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003337 # An exception instead may be sensible here, in one or more of
3338 # the cases.
3339 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003340 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003341
3342 # Find first Sunday in April.
3343 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3344 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3345
3346 # Find last Sunday in October.
3347 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3348 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3349
Tim Peters621818b2002-12-29 23:44:49 +00003350 # Can't compare naive to aware objects, so strip the timezone from
3351 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003352 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003353 return HOUR
3354 else:
3355 return ZERO
3356
Tim Peters521fc152002-12-31 17:36:56 +00003357Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3358Central = USTimeZone(-6, "Central", "CST", "CDT")
3359Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3360Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003361utc_real = FixedOffset(0, "UTC", 0)
3362# For better test coverage, we want another flavor of UTC that's west of
3363# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003364utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003365
3366class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003367 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003368 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003369 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003370
Tim Peters0bf60bd2003-01-08 20:40:01 +00003371 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003372
Tim Peters521fc152002-12-31 17:36:56 +00003373 # Check a time that's inside DST.
3374 def checkinside(self, dt, tz, utc, dston, dstoff):
3375 self.assertEqual(dt.dst(), HOUR)
3376
3377 # Conversion to our own timezone is always an identity.
3378 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003379
3380 asutc = dt.astimezone(utc)
3381 there_and_back = asutc.astimezone(tz)
3382
3383 # Conversion to UTC and back isn't always an identity here,
3384 # because there are redundant spellings (in local time) of
3385 # UTC time when DST begins: the clock jumps from 1:59:59
3386 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3387 # make sense then. The classes above treat 2:MM:SS as
3388 # daylight time then (it's "after 2am"), really an alias
3389 # for 1:MM:SS standard time. The latter form is what
3390 # conversion back from UTC produces.
3391 if dt.date() == dston.date() and dt.hour == 2:
3392 # We're in the redundant hour, and coming back from
3393 # UTC gives the 1:MM:SS standard-time spelling.
3394 self.assertEqual(there_and_back + HOUR, dt)
3395 # Although during was considered to be in daylight
3396 # time, there_and_back is not.
3397 self.assertEqual(there_and_back.dst(), ZERO)
3398 # They're the same times in UTC.
3399 self.assertEqual(there_and_back.astimezone(utc),
3400 dt.astimezone(utc))
3401 else:
3402 # We're not in the redundant hour.
3403 self.assertEqual(dt, there_and_back)
3404
Tim Peters327098a2003-01-20 22:54:38 +00003405 # Because we have a redundant spelling when DST begins, there is
3406 # (unforunately) an hour when DST ends that can't be spelled at all in
3407 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3408 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3409 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3410 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3411 # expressed in local time. Nevertheless, we want conversion back
3412 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003413 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003414 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003415 if dt.date() == dstoff.date() and dt.hour == 0:
3416 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003417 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003418 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3419 nexthour_utc += HOUR
3420 nexthour_tz = nexthour_utc.astimezone(tz)
3421 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003422 else:
Tim Peters327098a2003-01-20 22:54:38 +00003423 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003424
3425 # Check a time that's outside DST.
3426 def checkoutside(self, dt, tz, utc):
3427 self.assertEqual(dt.dst(), ZERO)
3428
3429 # Conversion to our own timezone is always an identity.
3430 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003431
3432 # Converting to UTC and back is an identity too.
3433 asutc = dt.astimezone(utc)
3434 there_and_back = asutc.astimezone(tz)
3435 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003436
Tim Peters1024bf82002-12-30 17:09:40 +00003437 def convert_between_tz_and_utc(self, tz, utc):
3438 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003439 # Because 1:MM on the day DST ends is taken as being standard time,
3440 # there is no spelling in tz for the last hour of daylight time.
3441 # For purposes of the test, the last hour of DST is 0:MM, which is
3442 # taken as being daylight time (and 1:MM is taken as being standard
3443 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003444 dstoff = self.dstoff.replace(tzinfo=tz)
3445 for delta in (timedelta(weeks=13),
3446 DAY,
3447 HOUR,
3448 timedelta(minutes=1),
3449 timedelta(microseconds=1)):
3450
Tim Peters521fc152002-12-31 17:36:56 +00003451 self.checkinside(dston, tz, utc, dston, dstoff)
3452 for during in dston + delta, dstoff - delta:
3453 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003454
Tim Peters521fc152002-12-31 17:36:56 +00003455 self.checkoutside(dstoff, tz, utc)
3456 for outside in dston - delta, dstoff + delta:
3457 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003458
Tim Peters621818b2002-12-29 23:44:49 +00003459 def test_easy(self):
3460 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003461 self.convert_between_tz_and_utc(Eastern, utc_real)
3462 self.convert_between_tz_and_utc(Pacific, utc_real)
3463 self.convert_between_tz_and_utc(Eastern, utc_fake)
3464 self.convert_between_tz_and_utc(Pacific, utc_fake)
3465 # The next is really dancing near the edge. It works because
3466 # Pacific and Eastern are far enough apart that their "problem
3467 # hours" don't overlap.
3468 self.convert_between_tz_and_utc(Eastern, Pacific)
3469 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003470 # OTOH, these fail! Don't enable them. The difficulty is that
3471 # the edge case tests assume that every hour is representable in
3472 # the "utc" class. This is always true for a fixed-offset tzinfo
3473 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3474 # For these adjacent DST-aware time zones, the range of time offsets
3475 # tested ends up creating hours in the one that aren't representable
3476 # in the other. For the same reason, we would see failures in the
3477 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3478 # offset deltas in convert_between_tz_and_utc().
3479 #
3480 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3481 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003482
Tim Petersf3615152003-01-01 21:51:37 +00003483 def test_tricky(self):
3484 # 22:00 on day before daylight starts.
3485 fourback = self.dston - timedelta(hours=4)
3486 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003487 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003488 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3489 # 2", we should get the 3 spelling.
3490 # If we plug 22:00 the day before into Eastern, it "looks like std
3491 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3492 # to 22:00 lands on 2:00, which makes no sense in local time (the
3493 # local clock jumps from 1 to 3). The point here is to make sure we
3494 # get the 3 spelling.
3495 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003496 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003497 self.assertEqual(expected, got)
3498
3499 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3500 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003501 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003502 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3503 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3504 # spelling.
3505 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003506 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003507 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003508
Tim Petersadf64202003-01-04 06:03:15 +00003509 # Now on the day DST ends, we want "repeat an hour" behavior.
3510 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3511 # EST 23:MM 0:MM 1:MM 2:MM
3512 # EDT 0:MM 1:MM 2:MM 3:MM
3513 # wall 0:MM 1:MM 1:MM 2:MM against these
3514 for utc in utc_real, utc_fake:
3515 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003516 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003517 # Convert that to UTC.
3518 first_std_hour -= tz.utcoffset(None)
3519 # Adjust for possibly fake UTC.
3520 asutc = first_std_hour + utc.utcoffset(None)
3521 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3522 # tz=Eastern.
3523 asutcbase = asutc.replace(tzinfo=utc)
3524 for tzhour in (0, 1, 1, 2):
3525 expectedbase = self.dstoff.replace(hour=tzhour)
3526 for minute in 0, 30, 59:
3527 expected = expectedbase.replace(minute=minute)
3528 asutc = asutcbase.replace(minute=minute)
3529 astz = asutc.astimezone(tz)
3530 self.assertEqual(astz.replace(tzinfo=None), expected)
3531 asutcbase += HOUR
3532
3533
Tim Peters710fb152003-01-02 19:35:54 +00003534 def test_bogus_dst(self):
3535 class ok(tzinfo):
3536 def utcoffset(self, dt): return HOUR
3537 def dst(self, dt): return HOUR
3538
3539 now = self.theclass.now().replace(tzinfo=utc_real)
3540 # Doesn't blow up.
3541 now.astimezone(ok())
3542
3543 # Does blow up.
3544 class notok(ok):
3545 def dst(self, dt): return None
3546 self.assertRaises(ValueError, now.astimezone, notok())
3547
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003548 # Sometimes blow up. In the following, tzinfo.dst()
Alexander Belopolsky5e307de2010-06-23 22:58:49 +00003549 # implementation may return None or not None depending on
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003550 # whether DST is assumed to be in effect. In this situation,
3551 # a ValueError should be raised by astimezone().
3552 class tricky_notok(ok):
3553 def dst(self, dt):
3554 if dt.year == 2000:
3555 return None
3556 else:
3557 return 10*HOUR
3558 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3559 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3560
Tim Peters52dcce22003-01-23 16:36:11 +00003561 def test_fromutc(self):
3562 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3563 now = datetime.utcnow().replace(tzinfo=utc_real)
3564 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3565 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3566 enow = Eastern.fromutc(now) # doesn't blow up
3567 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3568 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3569 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3570
3571 # Always converts UTC to standard time.
3572 class FauxUSTimeZone(USTimeZone):
3573 def fromutc(self, dt):
3574 return dt + self.stdoffset
3575 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3576
3577 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3578 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3579 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3580
3581 # Check around DST start.
3582 start = self.dston.replace(hour=4, tzinfo=Eastern)
3583 fstart = start.replace(tzinfo=FEastern)
3584 for wall in 23, 0, 1, 3, 4, 5:
3585 expected = start.replace(hour=wall)
3586 if wall == 23:
3587 expected -= timedelta(days=1)
3588 got = Eastern.fromutc(start)
3589 self.assertEqual(expected, got)
3590
3591 expected = fstart + FEastern.stdoffset
3592 got = FEastern.fromutc(fstart)
3593 self.assertEqual(expected, got)
3594
3595 # Ensure astimezone() calls fromutc() too.
3596 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3597 self.assertEqual(expected, got)
3598
3599 start += HOUR
3600 fstart += HOUR
3601
3602 # Check around DST end.
3603 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3604 fstart = start.replace(tzinfo=FEastern)
3605 for wall in 0, 1, 1, 2, 3, 4:
3606 expected = start.replace(hour=wall)
3607 got = Eastern.fromutc(start)
3608 self.assertEqual(expected, got)
3609
3610 expected = fstart + FEastern.stdoffset
3611 got = FEastern.fromutc(fstart)
3612 self.assertEqual(expected, got)
3613
3614 # Ensure astimezone() calls fromutc() too.
3615 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3616 self.assertEqual(expected, got)
3617
3618 start += HOUR
3619 fstart += HOUR
3620
Tim Peters710fb152003-01-02 19:35:54 +00003621
Tim Peters528ca532004-09-16 01:30:50 +00003622#############################################################################
3623# oddballs
3624
3625class Oddballs(unittest.TestCase):
3626
3627 def test_bug_1028306(self):
3628 # Trying to compare a date to a datetime should act like a mixed-
3629 # type comparison, despite that datetime is a subclass of date.
3630 as_date = date.today()
3631 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003632 self.assertTrue(as_date != as_datetime)
3633 self.assertTrue(as_datetime != as_date)
3634 self.assertTrue(not as_date == as_datetime)
3635 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003636 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3637 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3638 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3639 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3640 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3641 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3642 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3643 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3644
3645 # Neverthelss, comparison should work with the base-class (date)
3646 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003647 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003648 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003649 as_different = as_datetime.replace(day= different_day)
3650 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003651
3652 # And date should compare with other subclasses of date. If a
3653 # subclass wants to stop this, it's up to the subclass to do so.
3654 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3655 self.assertEqual(as_date, date_sc)
3656 self.assertEqual(date_sc, as_date)
3657
3658 # Ditto for datetimes.
3659 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3660 as_date.day, 0, 0, 0)
3661 self.assertEqual(as_datetime, datetime_sc)
3662 self.assertEqual(datetime_sc, as_datetime)
3663
Tim Peters2a799bf2002-12-16 20:18:38 +00003664def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003665 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003666
3667if __name__ == "__main__":
3668 test_main()