blob: f1b4ecf93a18ac220b9c988d18fefde2c10867ec [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Alexander Belopolskyd5442cd2010-05-26 20:00:12 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinson7c186e22010-04-20 22:32:49 +000010from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
Mark Dickinsona56c4672009-01-27 18:17:45 +000011
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000018from datetime import timezone
Tim Peters0bf60bd2003-01-08 20:40:01 +000019from datetime import date, datetime
Alexander Belopolskyca94f552010-06-17 18:30:34 +000020import time as _time
Tim Peters0bf60bd2003-01-08 20:40:01 +000021
Alexander Belopolsky33b94c92010-06-23 22:29:48 +000022pickle_choices = [(pickle, pickle, proto)
23 for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
24assert len(pickle_choices) == pickle.HIGHEST_PROTOCOL + 1
Guido van Rossum177e41a2003-01-30 22:06:23 +000025
Tim Peters68124bb2003-02-08 03:46:31 +000026# An arbitrary collection of objects of non-datetime types, for testing
27# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000028OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000029
Tim Peters2a799bf2002-12-16 20:18:38 +000030
Alexander Belopolsky1790bc42010-05-31 17:33:47 +000031# XXX Copied from test_float.
32INF = float("inf")
33NAN = float("nan")
34
35# decorator for skipping tests on non-IEEE 754 platforms
36requires_IEEE_754 = unittest.skipUnless(
37 float.__getformat__("double").startswith("IEEE"),
38 "test requires IEEE 754 doubles")
39
40
Tim Peters2a799bf2002-12-16 20:18:38 +000041#############################################################################
42# module tests
43
44class TestModule(unittest.TestCase):
45
46 def test_constants(self):
47 import datetime
48 self.assertEqual(datetime.MINYEAR, 1)
49 self.assertEqual(datetime.MAXYEAR, 9999)
50
51#############################################################################
52# tzinfo tests
53
54class FixedOffset(tzinfo):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000055
Tim Peters2a799bf2002-12-16 20:18:38 +000056 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000057 if isinstance(offset, int):
58 offset = timedelta(minutes=offset)
59 if isinstance(dstoffset, int):
60 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000061 self.__offset = offset
62 self.__name = name
63 self.__dstoffset = dstoffset
64 def __repr__(self):
65 return self.__name.lower()
66 def utcoffset(self, dt):
67 return self.__offset
68 def tzname(self, dt):
69 return self.__name
70 def dst(self, dt):
71 return self.__dstoffset
72
Tim Petersfb8472c2002-12-21 05:04:42 +000073class PicklableFixedOffset(FixedOffset):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000074
Tim Petersfb8472c2002-12-21 05:04:42 +000075 def __init__(self, offset=None, name=None, dstoffset=None):
76 FixedOffset.__init__(self, offset, name, dstoffset)
77
Tim Peters2a799bf2002-12-16 20:18:38 +000078class TestTZInfo(unittest.TestCase):
79
80 def test_non_abstractness(self):
81 # In order to allow subclasses to get pickled, the C implementation
82 # wasn't able to get away with having __init__ raise
83 # NotImplementedError.
84 useless = tzinfo()
85 dt = datetime.max
86 self.assertRaises(NotImplementedError, useless.tzname, dt)
87 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
88 self.assertRaises(NotImplementedError, useless.dst, dt)
89
90 def test_subclass_must_override(self):
91 class NotEnough(tzinfo):
92 def __init__(self, offset, name):
93 self.__offset = offset
94 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000095 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000096 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000097 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000098
99 dt = datetime.now()
100 self.assertRaises(NotImplementedError, ne.tzname, dt)
101 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
102 self.assertRaises(NotImplementedError, ne.dst, dt)
103
104 def test_normal(self):
105 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +0000106 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000107 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000108 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000109 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000110 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000111
112 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000113 # There's no point to pickling tzinfo objects on their own (they
114 # carry no data), but they need to be picklable anyway else
115 # concrete subclasses can't be pickled.
116 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000117 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000118 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000119 green = pickler.dumps(orig, proto)
120 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000121 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000122
123 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000124 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000125 offset = timedelta(minutes=-300)
Alexander Belopolsky1b7046b2010-06-23 21:40:15 +0000126 for otype, args in [
127 (PicklableFixedOffset, (offset, 'cookie')),
128 (timezone, (offset,)),
129 (timezone, (offset, "EST"))]:
130 orig = otype(*args)
131 oname = orig.tzname(None)
132 self.assertIsInstance(orig, tzinfo)
133 self.assertIs(type(orig), otype)
134 self.assertEqual(orig.utcoffset(None), offset)
135 self.assertEqual(orig.tzname(None), oname)
136 for pickler, unpickler, proto in pickle_choices:
137 green = pickler.dumps(orig, proto)
138 derived = unpickler.loads(green)
139 self.assertIsInstance(derived, tzinfo)
140 self.assertIs(type(derived), otype)
141 self.assertEqual(derived.utcoffset(None), offset)
142 self.assertEqual(derived.tzname(None), oname)
Tim Peters2a799bf2002-12-16 20:18:38 +0000143
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000144class TestTimeZone(unittest.TestCase):
145
146 def setUp(self):
147 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
148 self.EST = timezone(-timedelta(hours=5), 'EST')
149 self.DT = datetime(2010, 1, 1)
150
151 def test_str(self):
152 for tz in [self.ACDT, self.EST, timezone.utc,
153 timezone.min, timezone.max]:
154 self.assertEqual(str(tz), tz.tzname(None))
155
156 def test_class_members(self):
157 limit = timedelta(hours=23, minutes=59)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000158 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
159 self.assertEqual(timezone.min.utcoffset(None), -limit)
160 self.assertEqual(timezone.max.utcoffset(None), limit)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000161
162
163 def test_constructor(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000164 self.assertEqual(timezone.utc, timezone(timedelta(0)))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000165 # invalid offsets
166 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
167 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
168 self.assertRaises(ValueError, timezone, invalid)
169 self.assertRaises(ValueError, timezone, -invalid)
170
171 with self.assertRaises(TypeError): timezone(None)
172 with self.assertRaises(TypeError): timezone(42)
173 with self.assertRaises(TypeError): timezone(ZERO, None)
174 with self.assertRaises(TypeError): timezone(ZERO, 42)
175
176 def test_inheritance(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000177 self.assertIsInstance(timezone.utc, tzinfo)
178 self.assertIsInstance(self.EST, tzinfo)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000179
180 def test_utcoffset(self):
181 dummy = self.DT
182 for h in [0, 1.5, 12]:
183 offset = h * HOUR
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000184 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
185 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000186
187 with self.assertRaises(TypeError): self.EST.utcoffset('')
188 with self.assertRaises(TypeError): self.EST.utcoffset(5)
189
190
191 def test_dst(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000192 self.assertEqual(None, timezone.utc.dst(self.DT))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000193
194 with self.assertRaises(TypeError): self.EST.dst('')
195 with self.assertRaises(TypeError): self.EST.dst(5)
196
197 def test_tzname(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000198 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
199 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
200 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
201 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
202 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000203
204 with self.assertRaises(TypeError): self.EST.tzname('')
205 with self.assertRaises(TypeError): self.EST.tzname(5)
206
207 def test_fromutc(self):
208 with self.assertRaises(ValueError):
209 timezone.utc.fromutc(self.DT)
210 for tz in [self.EST, self.ACDT, Eastern]:
211 utctime = self.DT.replace(tzinfo=tz)
212 local = tz.fromutc(utctime)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000213 self.assertEqual(local - utctime, tz.utcoffset(local))
214 self.assertEqual(local,
215 self.DT.replace(tzinfo=timezone.utc))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000216
217 def test_comparison(self):
218 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
219 self.assertEqual(timezone(HOUR), timezone(HOUR))
220 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
221 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
222 self.assertIn(timezone(ZERO), {timezone(ZERO)})
223
224 def test_aware_datetime(self):
225 # test that timezone instances can be used by datetime
226 t = datetime(1, 1, 1)
227 for tz in [timezone.min, timezone.max, timezone.utc]:
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000228 self.assertEqual(tz.tzname(t),
229 t.replace(tzinfo=tz).tzname())
230 self.assertEqual(tz.utcoffset(t),
231 t.replace(tzinfo=tz).utcoffset())
232 self.assertEqual(tz.dst(t),
233 t.replace(tzinfo=tz).dst())
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000234
Tim Peters2a799bf2002-12-16 20:18:38 +0000235#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000236# Base clase for testing a particular aspect of timedelta, time, date and
237# datetime comparisons.
238
Guido van Rossumd8faa362007-04-27 19:54:29 +0000239class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000240 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
241
242 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
243 # legit constructor.
244
245 def test_harmless_mixed_comparison(self):
246 me = self.theclass(1, 1, 1)
247
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000248 self.assertFalse(me == ())
249 self.assertTrue(me != ())
250 self.assertFalse(() == me)
251 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000252
Benjamin Peterson577473f2010-01-19 00:09:57 +0000253 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000254 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000255
256 def test_harmful_mixed_comparison(self):
257 me = self.theclass(1, 1, 1)
258
259 self.assertRaises(TypeError, lambda: me < ())
260 self.assertRaises(TypeError, lambda: me <= ())
261 self.assertRaises(TypeError, lambda: me > ())
262 self.assertRaises(TypeError, lambda: me >= ())
263
264 self.assertRaises(TypeError, lambda: () < me)
265 self.assertRaises(TypeError, lambda: () <= me)
266 self.assertRaises(TypeError, lambda: () > me)
267 self.assertRaises(TypeError, lambda: () >= me)
268
Tim Peters07534a62003-02-07 22:50:28 +0000269#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000270# timedelta tests
271
Guido van Rossumd8faa362007-04-27 19:54:29 +0000272class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000273
274 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000275
276 def test_constructor(self):
277 eq = self.assertEqual
278 td = timedelta
279
280 # Check keyword args to constructor
281 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
282 milliseconds=0, microseconds=0))
283 eq(td(1), td(days=1))
284 eq(td(0, 1), td(seconds=1))
285 eq(td(0, 0, 1), td(microseconds=1))
286 eq(td(weeks=1), td(days=7))
287 eq(td(days=1), td(hours=24))
288 eq(td(hours=1), td(minutes=60))
289 eq(td(minutes=1), td(seconds=60))
290 eq(td(seconds=1), td(milliseconds=1000))
291 eq(td(milliseconds=1), td(microseconds=1000))
292
293 # Check float args to constructor
294 eq(td(weeks=1.0/7), td(days=1))
295 eq(td(days=1.0/24), td(hours=1))
296 eq(td(hours=1.0/60), td(minutes=1))
297 eq(td(minutes=1.0/60), td(seconds=1))
298 eq(td(seconds=0.001), td(milliseconds=1))
299 eq(td(milliseconds=0.001), td(microseconds=1))
300
301 def test_computations(self):
302 eq = self.assertEqual
303 td = timedelta
304
305 a = td(7) # One week
306 b = td(0, 60) # One minute
307 c = td(0, 0, 1000) # One millisecond
308 eq(a+b+c, td(7, 60, 1000))
309 eq(a-b, td(6, 24*3600 - 60))
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000310 eq(b.__rsub__(a), td(6, 24*3600 - 60))
Tim Peters2a799bf2002-12-16 20:18:38 +0000311 eq(-a, td(-7))
312 eq(+a, td(7))
313 eq(-b, td(-1, 24*3600 - 60))
314 eq(-c, td(-1, 24*3600 - 1, 999000))
315 eq(abs(a), a)
316 eq(abs(-a), a)
317 eq(td(6, 24*3600), a)
318 eq(td(0, 0, 60*1000000), b)
319 eq(a*10, td(70))
320 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000321 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000322 eq(b*10, td(0, 600))
323 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000324 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000325 eq(c*10, td(0, 0, 10000))
326 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000327 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000328 eq(a*-1, -a)
329 eq(b*-2, -b-b)
330 eq(c*-2, -c+-c)
331 eq(b*(60*24), (b*60)*24)
332 eq(b*(60*24), (60*b)*24)
333 eq(c*1000, td(0, 1))
334 eq(1000*c, td(0, 1))
335 eq(a//7, td(1))
336 eq(b//10, td(0, 6))
337 eq(c//1000, td(0, 0, 1))
338 eq(a//10, td(0, 7*24*360))
339 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000340 eq(a/0.5, td(14))
341 eq(b/0.5, td(0, 120))
342 eq(a/7, td(1))
343 eq(b/10, td(0, 6))
344 eq(c/1000, td(0, 0, 1))
345 eq(a/10, td(0, 7*24*360))
346 eq(a/3600000, td(0, 0, 7*24*1000))
347
348 # Multiplication by float
349 us = td(microseconds=1)
350 eq((3*us) * 0.5, 2*us)
351 eq((5*us) * 0.5, 2*us)
352 eq(0.5 * (3*us), 2*us)
353 eq(0.5 * (5*us), 2*us)
354 eq((-3*us) * 0.5, -2*us)
355 eq((-5*us) * 0.5, -2*us)
356
357 # Division by int and float
358 eq((3*us) / 2, 2*us)
359 eq((5*us) / 2, 2*us)
360 eq((-3*us) / 2.0, -2*us)
361 eq((-5*us) / 2.0, -2*us)
362 eq((3*us) / -2, -2*us)
363 eq((5*us) / -2, -2*us)
364 eq((3*us) / -2.0, -2*us)
365 eq((5*us) / -2.0, -2*us)
366 for i in range(-10, 10):
367 eq((i*us/3)//us, round(i/3))
368 for i in range(-10, 10):
369 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000370
371 def test_disallowed_computations(self):
372 a = timedelta(42)
373
Mark Dickinson5c2db372009-12-05 20:28:34 +0000374 # Add/sub ints or floats should be illegal
375 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000376 self.assertRaises(TypeError, lambda: a+i)
377 self.assertRaises(TypeError, lambda: a-i)
378 self.assertRaises(TypeError, lambda: i+a)
379 self.assertRaises(TypeError, lambda: i-a)
380
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000381 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000382 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000383 zero = 0
384 self.assertRaises(TypeError, lambda: zero // a)
385 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000386 self.assertRaises(ZeroDivisionError, lambda: a / zero)
387 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000388 self.assertRaises(TypeError, lambda: a / '')
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000389
390 @requires_IEEE_754
391 def test_disallowed_special(self):
392 a = timedelta(42)
393 self.assertRaises(ValueError, a.__mul__, NAN)
394 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000395
396 def test_basic_attributes(self):
397 days, seconds, us = 1, 7, 31
398 td = timedelta(days, seconds, us)
399 self.assertEqual(td.days, days)
400 self.assertEqual(td.seconds, seconds)
401 self.assertEqual(td.microseconds, us)
402
Antoine Pitroube6859d2009-11-25 23:02:32 +0000403 def test_total_seconds(self):
404 td = timedelta(days=365)
405 self.assertEqual(td.total_seconds(), 31536000.0)
406 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
407 td = timedelta(seconds=total_seconds)
408 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000409 # Issue8644: Test that td.total_seconds() has the same
410 # accuracy as td / timedelta(seconds=1).
411 for ms in [-1, -2, -123]:
412 td = timedelta(microseconds=ms)
413 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000414
Tim Peters2a799bf2002-12-16 20:18:38 +0000415 def test_carries(self):
416 t1 = timedelta(days=100,
417 weeks=-7,
418 hours=-24*(100-49),
419 minutes=-3,
420 seconds=12,
421 microseconds=(3*60 - 12) * 1e6 + 1)
422 t2 = timedelta(microseconds=1)
423 self.assertEqual(t1, t2)
424
425 def test_hash_equality(self):
426 t1 = timedelta(days=100,
427 weeks=-7,
428 hours=-24*(100-49),
429 minutes=-3,
430 seconds=12,
431 microseconds=(3*60 - 12) * 1000000)
432 t2 = timedelta()
433 self.assertEqual(hash(t1), hash(t2))
434
435 t1 += timedelta(weeks=7)
436 t2 += timedelta(days=7*7)
437 self.assertEqual(t1, t2)
438 self.assertEqual(hash(t1), hash(t2))
439
440 d = {t1: 1}
441 d[t2] = 2
442 self.assertEqual(len(d), 1)
443 self.assertEqual(d[t1], 2)
444
445 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000446 args = 12, 34, 56
447 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000448 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000449 green = pickler.dumps(orig, proto)
450 derived = unpickler.loads(green)
451 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000452
453 def test_compare(self):
454 t1 = timedelta(2, 3, 4)
455 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000456 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000457 self.assertTrue(t1 <= t2)
458 self.assertTrue(t1 >= t2)
459 self.assertTrue(not t1 != t2)
460 self.assertTrue(not t1 < t2)
461 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000462
463 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
464 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000465 self.assertTrue(t1 < t2)
466 self.assertTrue(t2 > t1)
467 self.assertTrue(t1 <= t2)
468 self.assertTrue(t2 >= t1)
469 self.assertTrue(t1 != t2)
470 self.assertTrue(t2 != t1)
471 self.assertTrue(not t1 == t2)
472 self.assertTrue(not t2 == t1)
473 self.assertTrue(not t1 > t2)
474 self.assertTrue(not t2 < t1)
475 self.assertTrue(not t1 >= t2)
476 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000477
Tim Peters68124bb2003-02-08 03:46:31 +0000478 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000479 self.assertEqual(t1 == badarg, False)
480 self.assertEqual(t1 != badarg, True)
481 self.assertEqual(badarg == t1, False)
482 self.assertEqual(badarg != t1, True)
483
Tim Peters2a799bf2002-12-16 20:18:38 +0000484 self.assertRaises(TypeError, lambda: t1 <= badarg)
485 self.assertRaises(TypeError, lambda: t1 < badarg)
486 self.assertRaises(TypeError, lambda: t1 > badarg)
487 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000488 self.assertRaises(TypeError, lambda: badarg <= t1)
489 self.assertRaises(TypeError, lambda: badarg < t1)
490 self.assertRaises(TypeError, lambda: badarg > t1)
491 self.assertRaises(TypeError, lambda: badarg >= t1)
492
493 def test_str(self):
494 td = timedelta
495 eq = self.assertEqual
496
497 eq(str(td(1)), "1 day, 0:00:00")
498 eq(str(td(-1)), "-1 day, 0:00:00")
499 eq(str(td(2)), "2 days, 0:00:00")
500 eq(str(td(-2)), "-2 days, 0:00:00")
501
502 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
503 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
504 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
505 "-210 days, 23:12:34")
506
507 eq(str(td(milliseconds=1)), "0:00:00.001000")
508 eq(str(td(microseconds=3)), "0:00:00.000003")
509
510 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
511 microseconds=999999)),
512 "999999999 days, 23:59:59.999999")
513
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000514 def test_repr(self):
515 name = 'datetime.' + self.theclass.__name__
516 self.assertEqual(repr(self.theclass(1)),
517 "%s(1)" % name)
518 self.assertEqual(repr(self.theclass(10, 2)),
519 "%s(10, 2)" % name)
520 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
521 "%s(-10, 2, 400000)" % name)
522
Tim Peters2a799bf2002-12-16 20:18:38 +0000523 def test_roundtrip(self):
524 for td in (timedelta(days=999999999, hours=23, minutes=59,
525 seconds=59, microseconds=999999),
526 timedelta(days=-999999999),
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000527 timedelta(days=-999999999, seconds=1),
Tim Peters2a799bf2002-12-16 20:18:38 +0000528 timedelta(days=1, seconds=2, microseconds=3)):
529
530 # Verify td -> string -> td identity.
531 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000532 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000533 s = s[9:]
534 td2 = eval(s)
535 self.assertEqual(td, td2)
536
537 # Verify identity via reconstructing from pieces.
538 td2 = timedelta(td.days, td.seconds, td.microseconds)
539 self.assertEqual(td, td2)
540
541 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000542 self.assertIsInstance(timedelta.min, timedelta)
543 self.assertIsInstance(timedelta.max, timedelta)
544 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000545 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000546 self.assertEqual(timedelta.min, timedelta(-999999999))
547 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
548 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
549
550 def test_overflow(self):
551 tiny = timedelta.resolution
552
553 td = timedelta.min + tiny
554 td -= tiny # no problem
555 self.assertRaises(OverflowError, td.__sub__, tiny)
556 self.assertRaises(OverflowError, td.__add__, -tiny)
557
558 td = timedelta.max - tiny
559 td += tiny # no problem
560 self.assertRaises(OverflowError, td.__add__, tiny)
561 self.assertRaises(OverflowError, td.__sub__, -tiny)
562
563 self.assertRaises(OverflowError, lambda: -timedelta.max)
564
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000565 day = timedelta(1)
566 self.assertRaises(OverflowError, day.__mul__, 10**9)
567 self.assertRaises(OverflowError, day.__mul__, 1e9)
568 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
569 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
570 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
571
572 @requires_IEEE_754
573 def _test_overflow_special(self):
574 day = timedelta(1)
575 self.assertRaises(OverflowError, day.__mul__, INF)
576 self.assertRaises(OverflowError, day.__mul__, -INF)
577
Tim Peters2a799bf2002-12-16 20:18:38 +0000578 def test_microsecond_rounding(self):
579 td = timedelta
580 eq = self.assertEqual
581
582 # Single-field rounding.
583 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
584 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
585 eq(td(milliseconds=0.6/1000), td(microseconds=1))
586 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
587
588 # Rounding due to contributions from more than one field.
589 us_per_hour = 3600e6
590 us_per_day = us_per_hour * 24
591 eq(td(days=.4/us_per_day), td(0))
592 eq(td(hours=.2/us_per_hour), td(0))
593 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
594
595 eq(td(days=-.4/us_per_day), td(0))
596 eq(td(hours=-.2/us_per_hour), td(0))
597 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
598
599 def test_massive_normalization(self):
600 td = timedelta(microseconds=-1)
601 self.assertEqual((td.days, td.seconds, td.microseconds),
602 (-1, 24*3600-1, 999999))
603
604 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000605 self.assertTrue(timedelta(1))
606 self.assertTrue(timedelta(0, 1))
607 self.assertTrue(timedelta(0, 0, 1))
608 self.assertTrue(timedelta(microseconds=1))
609 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000610
Tim Petersb0c854d2003-05-17 15:57:00 +0000611 def test_subclass_timedelta(self):
612
613 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000614 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000615 def from_td(td):
616 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000617
618 def as_hours(self):
619 sum = (self.days * 24 +
620 self.seconds / 3600.0 +
621 self.microseconds / 3600e6)
622 return round(sum)
623
624 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000625 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000626 self.assertEqual(t1.as_hours(), 24)
627
628 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000629 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000630 self.assertEqual(t2.as_hours(), -25)
631
632 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000633 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000634 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000635 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000636 self.assertEqual(t3.days, t4.days)
637 self.assertEqual(t3.seconds, t4.seconds)
638 self.assertEqual(t3.microseconds, t4.microseconds)
639 self.assertEqual(str(t3), str(t4))
640 self.assertEqual(t4.as_hours(), -1)
641
Mark Dickinson7c186e22010-04-20 22:32:49 +0000642 def test_division(self):
643 t = timedelta(hours=1, minutes=24, seconds=19)
644 second = timedelta(seconds=1)
645 self.assertEqual(t / second, 5059.0)
646 self.assertEqual(t // second, 5059)
647
648 t = timedelta(minutes=2, seconds=30)
649 minute = timedelta(minutes=1)
650 self.assertEqual(t / minute, 2.5)
651 self.assertEqual(t // minute, 2)
652
653 zerotd = timedelta(0)
654 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
655 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
656
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000657 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000658 # note: floor division of a timedelta by an integer *is*
659 # currently permitted.
660
661 def test_remainder(self):
662 t = timedelta(minutes=2, seconds=30)
663 minute = timedelta(minutes=1)
664 r = t % minute
665 self.assertEqual(r, timedelta(seconds=30))
666
667 t = timedelta(minutes=-2, seconds=30)
668 r = t % minute
669 self.assertEqual(r, timedelta(seconds=30))
670
671 zerotd = timedelta(0)
672 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
673
674 self.assertRaises(TypeError, mod, t, 10)
675
676 def test_divmod(self):
677 t = timedelta(minutes=2, seconds=30)
678 minute = timedelta(minutes=1)
679 q, r = divmod(t, minute)
680 self.assertEqual(q, 2)
681 self.assertEqual(r, timedelta(seconds=30))
682
683 t = timedelta(minutes=-2, seconds=30)
684 q, r = divmod(t, minute)
685 self.assertEqual(q, -2)
686 self.assertEqual(r, timedelta(seconds=30))
687
688 zerotd = timedelta(0)
689 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
690
691 self.assertRaises(TypeError, divmod, t, 10)
692
693
Tim Peters2a799bf2002-12-16 20:18:38 +0000694#############################################################################
695# date tests
696
697class TestDateOnly(unittest.TestCase):
698 # Tests here won't pass if also run on datetime objects, so don't
699 # subclass this to test datetimes too.
700
701 def test_delta_non_days_ignored(self):
702 dt = date(2000, 1, 2)
703 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
704 microseconds=5)
705 days = timedelta(delta.days)
706 self.assertEqual(days, timedelta(1))
707
708 dt2 = dt + delta
709 self.assertEqual(dt2, dt + days)
710
711 dt2 = delta + dt
712 self.assertEqual(dt2, dt + days)
713
714 dt2 = dt - delta
715 self.assertEqual(dt2, dt - days)
716
717 delta = -delta
718 days = timedelta(delta.days)
719 self.assertEqual(days, timedelta(-2))
720
721 dt2 = dt + delta
722 self.assertEqual(dt2, dt + days)
723
724 dt2 = delta + dt
725 self.assertEqual(dt2, dt + days)
726
727 dt2 = dt - delta
728 self.assertEqual(dt2, dt - days)
729
Tim Peters604c0132004-06-07 23:04:33 +0000730class SubclassDate(date):
731 sub_var = 1
732
Guido van Rossumd8faa362007-04-27 19:54:29 +0000733class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000734 # Tests here should pass for both dates and datetimes, except for a
735 # few tests that TestDateTime overrides.
736
737 theclass = date
738
739 def test_basic_attributes(self):
740 dt = self.theclass(2002, 3, 1)
741 self.assertEqual(dt.year, 2002)
742 self.assertEqual(dt.month, 3)
743 self.assertEqual(dt.day, 1)
744
745 def test_roundtrip(self):
746 for dt in (self.theclass(1, 2, 3),
747 self.theclass.today()):
748 # Verify dt -> string -> date identity.
749 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000750 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000751 s = s[9:]
752 dt2 = eval(s)
753 self.assertEqual(dt, dt2)
754
755 # Verify identity via reconstructing from pieces.
756 dt2 = self.theclass(dt.year, dt.month, dt.day)
757 self.assertEqual(dt, dt2)
758
759 def test_ordinal_conversions(self):
760 # Check some fixed values.
761 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
762 (1, 12, 31, 365),
763 (2, 1, 1, 366),
764 # first example from "Calendrical Calculations"
765 (1945, 11, 12, 710347)]:
766 d = self.theclass(y, m, d)
767 self.assertEqual(n, d.toordinal())
768 fromord = self.theclass.fromordinal(n)
769 self.assertEqual(d, fromord)
770 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000771 # if we're checking something fancier than a date, verify
772 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000773 self.assertEqual(fromord.hour, 0)
774 self.assertEqual(fromord.minute, 0)
775 self.assertEqual(fromord.second, 0)
776 self.assertEqual(fromord.microsecond, 0)
777
Tim Peters0bf60bd2003-01-08 20:40:01 +0000778 # Check first and last days of year spottily across the whole
779 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000780 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000781 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
782 d = self.theclass(year, 1, 1)
783 n = d.toordinal()
784 d2 = self.theclass.fromordinal(n)
785 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000786 # Verify that moving back a day gets to the end of year-1.
787 if year > 1:
788 d = self.theclass.fromordinal(n-1)
789 d2 = self.theclass(year-1, 12, 31)
790 self.assertEqual(d, d2)
791 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000792
793 # Test every day in a leap-year and a non-leap year.
794 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
795 for year, isleap in (2000, True), (2002, False):
796 n = self.theclass(year, 1, 1).toordinal()
797 for month, maxday in zip(range(1, 13), dim):
798 if month == 2 and isleap:
799 maxday += 1
800 for day in range(1, maxday+1):
801 d = self.theclass(year, month, day)
802 self.assertEqual(d.toordinal(), n)
803 self.assertEqual(d, self.theclass.fromordinal(n))
804 n += 1
805
806 def test_extreme_ordinals(self):
807 a = self.theclass.min
808 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
809 aord = a.toordinal()
810 b = a.fromordinal(aord)
811 self.assertEqual(a, b)
812
813 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
814
815 b = a + timedelta(days=1)
816 self.assertEqual(b.toordinal(), aord + 1)
817 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
818
819 a = self.theclass.max
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 def test_bad_constructor_arguments(self):
832 # bad years
833 self.theclass(MINYEAR, 1, 1) # no exception
834 self.theclass(MAXYEAR, 1, 1) # no exception
835 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
836 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
837 # bad months
838 self.theclass(2000, 1, 1) # no exception
839 self.theclass(2000, 12, 1) # no exception
840 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
841 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
842 # bad days
843 self.theclass(2000, 2, 29) # no exception
844 self.theclass(2004, 2, 29) # no exception
845 self.theclass(2400, 2, 29) # no exception
846 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
847 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
848 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
849 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
850 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
851 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
852
853 def test_hash_equality(self):
854 d = self.theclass(2000, 12, 31)
855 # same thing
856 e = self.theclass(2000, 12, 31)
857 self.assertEqual(d, e)
858 self.assertEqual(hash(d), hash(e))
859
860 dic = {d: 1}
861 dic[e] = 2
862 self.assertEqual(len(dic), 1)
863 self.assertEqual(dic[d], 2)
864 self.assertEqual(dic[e], 2)
865
866 d = self.theclass(2001, 1, 1)
867 # same thing
868 e = self.theclass(2001, 1, 1)
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 def test_computations(self):
879 a = self.theclass(2002, 1, 31)
880 b = self.theclass(1956, 1, 31)
881
882 diff = a-b
883 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
884 self.assertEqual(diff.seconds, 0)
885 self.assertEqual(diff.microseconds, 0)
886
887 day = timedelta(1)
888 week = timedelta(7)
889 a = self.theclass(2002, 3, 2)
890 self.assertEqual(a + day, self.theclass(2002, 3, 3))
891 self.assertEqual(day + a, self.theclass(2002, 3, 3))
892 self.assertEqual(a - day, self.theclass(2002, 3, 1))
893 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
894 self.assertEqual(a + week, self.theclass(2002, 3, 9))
895 self.assertEqual(a - week, self.theclass(2002, 2, 23))
896 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
897 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
898 self.assertEqual((a + week) - a, week)
899 self.assertEqual((a + day) - a, day)
900 self.assertEqual((a - week) - a, -week)
901 self.assertEqual((a - day) - a, -day)
902 self.assertEqual(a - (a + week), -week)
903 self.assertEqual(a - (a + day), -day)
904 self.assertEqual(a - (a - week), week)
905 self.assertEqual(a - (a - day), day)
906
Mark Dickinson5c2db372009-12-05 20:28:34 +0000907 # Add/sub ints or floats should be illegal
908 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000909 self.assertRaises(TypeError, lambda: a+i)
910 self.assertRaises(TypeError, lambda: a-i)
911 self.assertRaises(TypeError, lambda: i+a)
912 self.assertRaises(TypeError, lambda: i-a)
913
914 # delta - date is senseless.
915 self.assertRaises(TypeError, lambda: day - a)
916 # mixing date and (delta or date) via * or // is senseless
917 self.assertRaises(TypeError, lambda: day * a)
918 self.assertRaises(TypeError, lambda: a * day)
919 self.assertRaises(TypeError, lambda: day // a)
920 self.assertRaises(TypeError, lambda: a // day)
921 self.assertRaises(TypeError, lambda: a * a)
922 self.assertRaises(TypeError, lambda: a // a)
923 # date + date is senseless
924 self.assertRaises(TypeError, lambda: a + a)
925
926 def test_overflow(self):
927 tiny = self.theclass.resolution
928
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000929 for delta in [tiny, timedelta(1), timedelta(2)]:
930 dt = self.theclass.min + delta
931 dt -= delta # no problem
932 self.assertRaises(OverflowError, dt.__sub__, delta)
933 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000934
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000935 dt = self.theclass.max - delta
936 dt += delta # no problem
937 self.assertRaises(OverflowError, dt.__add__, delta)
938 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000939
940 def test_fromtimestamp(self):
941 import time
942
943 # Try an arbitrary fixed value.
944 year, month, day = 1999, 9, 19
945 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
946 d = self.theclass.fromtimestamp(ts)
947 self.assertEqual(d.year, year)
948 self.assertEqual(d.month, month)
949 self.assertEqual(d.day, day)
950
Tim Peters1b6f7a92004-06-20 02:50:16 +0000951 def test_insane_fromtimestamp(self):
952 # It's possible that some platform maps time_t to double,
953 # and that this test will fail there. This test should
954 # exempt such platforms (provided they return reasonable
955 # results!).
956 for insane in -1e200, 1e200:
957 self.assertRaises(ValueError, self.theclass.fromtimestamp,
958 insane)
959
Tim Peters2a799bf2002-12-16 20:18:38 +0000960 def test_today(self):
961 import time
962
963 # We claim that today() is like fromtimestamp(time.time()), so
964 # prove it.
965 for dummy in range(3):
966 today = self.theclass.today()
967 ts = time.time()
968 todayagain = self.theclass.fromtimestamp(ts)
969 if today == todayagain:
970 break
971 # There are several legit reasons that could fail:
972 # 1. It recently became midnight, between the today() and the
973 # time() calls.
974 # 2. The platform time() has such fine resolution that we'll
975 # never get the same value twice.
976 # 3. The platform time() has poor resolution, and we just
977 # happened to call today() right before a resolution quantum
978 # boundary.
979 # 4. The system clock got fiddled between calls.
980 # In any case, wait a little while and try again.
981 time.sleep(0.1)
982
983 # It worked or it didn't. If it didn't, assume it's reason #2, and
984 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000985 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000986 abs(todayagain - today) < timedelta(seconds=0.5))
987
988 def test_weekday(self):
989 for i in range(7):
990 # March 4, 2002 is a Monday
991 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
992 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
993 # January 2, 1956 is a Monday
994 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
995 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
996
997 def test_isocalendar(self):
998 # Check examples from
999 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1000 for i in range(7):
1001 d = self.theclass(2003, 12, 22+i)
1002 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1003 d = self.theclass(2003, 12, 29) + timedelta(i)
1004 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1005 d = self.theclass(2004, 1, 5+i)
1006 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1007 d = self.theclass(2009, 12, 21+i)
1008 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1009 d = self.theclass(2009, 12, 28) + timedelta(i)
1010 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1011 d = self.theclass(2010, 1, 4+i)
1012 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1013
1014 def test_iso_long_years(self):
1015 # Calculate long ISO years and compare to table from
1016 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1017 ISO_LONG_YEARS_TABLE = """
1018 4 32 60 88
1019 9 37 65 93
1020 15 43 71 99
1021 20 48 76
1022 26 54 82
1023
1024 105 133 161 189
1025 111 139 167 195
1026 116 144 172
1027 122 150 178
1028 128 156 184
1029
1030 201 229 257 285
1031 207 235 263 291
1032 212 240 268 296
1033 218 246 274
1034 224 252 280
1035
1036 303 331 359 387
1037 308 336 364 392
1038 314 342 370 398
1039 320 348 376
1040 325 353 381
1041 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001042 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001043 L = []
1044 for i in range(400):
1045 d = self.theclass(2000+i, 12, 31)
1046 d1 = self.theclass(1600+i, 12, 31)
1047 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1048 if d.isocalendar()[1] == 53:
1049 L.append(i)
1050 self.assertEqual(L, iso_long_years)
1051
1052 def test_isoformat(self):
1053 t = self.theclass(2, 3, 2)
1054 self.assertEqual(t.isoformat(), "0002-03-02")
1055
1056 def test_ctime(self):
1057 t = self.theclass(2002, 3, 2)
1058 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1059
1060 def test_strftime(self):
1061 t = self.theclass(2005, 3, 2)
1062 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001063 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001064 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001065
1066 self.assertRaises(TypeError, t.strftime) # needs an arg
1067 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1068 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1069
Georg Brandlf78e02b2008-06-10 17:40:04 +00001070 # test that unicode input is allowed (issue 2782)
1071 self.assertEqual(t.strftime("%m"), "03")
1072
Tim Peters2a799bf2002-12-16 20:18:38 +00001073 # A naive object replaces %z and %Z w/ empty strings.
1074 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1075
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001076 #make sure that invalid format specifiers are handled correctly
1077 #self.assertRaises(ValueError, t.strftime, "%e")
1078 #self.assertRaises(ValueError, t.strftime, "%")
1079 #self.assertRaises(ValueError, t.strftime, "%#")
1080
1081 #oh well, some systems just ignore those invalid ones.
1082 #at least, excercise them to make sure that no crashes
1083 #are generated
1084 for f in ["%e", "%", "%#"]:
1085 try:
1086 t.strftime(f)
1087 except ValueError:
1088 pass
1089
1090 #check that this standard extension works
1091 t.strftime("%f")
1092
Georg Brandlf78e02b2008-06-10 17:40:04 +00001093
Eric Smith1ba31142007-09-11 18:06:02 +00001094 def test_format(self):
1095 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001096 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001097
1098 # check that a derived class's __str__() gets called
1099 class A(self.theclass):
1100 def __str__(self):
1101 return 'A'
1102 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001103 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001104
1105 # check that a derived class's strftime gets called
1106 class B(self.theclass):
1107 def strftime(self, format_spec):
1108 return 'B'
1109 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001110 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001111
1112 for fmt in ["m:%m d:%d y:%y",
1113 "m:%m d:%d y:%y H:%H M:%M S:%S",
1114 "%z %Z",
1115 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001116 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1117 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1118 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001119
Tim Peters2a799bf2002-12-16 20:18:38 +00001120 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001121 # XXX: Should min and max respect subclassing?
1122 if issubclass(self.theclass, datetime):
1123 expected_class = datetime
1124 else:
1125 expected_class = date
1126 self.assertIsInstance(self.theclass.min, expected_class)
1127 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001128 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001129 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001130
1131 def test_extreme_timedelta(self):
1132 big = self.theclass.max - self.theclass.min
1133 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1134 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1135 # n == 315537897599999999 ~= 2**58.13
1136 justasbig = timedelta(0, 0, n)
1137 self.assertEqual(big, justasbig)
1138 self.assertEqual(self.theclass.min + big, self.theclass.max)
1139 self.assertEqual(self.theclass.max - big, self.theclass.min)
1140
1141 def test_timetuple(self):
1142 for i in range(7):
1143 # January 2, 1956 is a Monday (0)
1144 d = self.theclass(1956, 1, 2+i)
1145 t = d.timetuple()
1146 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1147 # February 1, 1956 is a Wednesday (2)
1148 d = self.theclass(1956, 2, 1+i)
1149 t = d.timetuple()
1150 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1151 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1152 # of the year.
1153 d = self.theclass(1956, 3, 1+i)
1154 t = d.timetuple()
1155 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1156 self.assertEqual(t.tm_year, 1956)
1157 self.assertEqual(t.tm_mon, 3)
1158 self.assertEqual(t.tm_mday, 1+i)
1159 self.assertEqual(t.tm_hour, 0)
1160 self.assertEqual(t.tm_min, 0)
1161 self.assertEqual(t.tm_sec, 0)
1162 self.assertEqual(t.tm_wday, (3+i)%7)
1163 self.assertEqual(t.tm_yday, 61+i)
1164 self.assertEqual(t.tm_isdst, -1)
1165
1166 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001167 args = 6, 7, 23
1168 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001169 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001170 green = pickler.dumps(orig, proto)
1171 derived = unpickler.loads(green)
1172 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001173
1174 def test_compare(self):
1175 t1 = self.theclass(2, 3, 4)
1176 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001177 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001178 self.assertTrue(t1 <= t2)
1179 self.assertTrue(t1 >= t2)
1180 self.assertTrue(not t1 != t2)
1181 self.assertTrue(not t1 < t2)
1182 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001183
1184 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1185 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001186 self.assertTrue(t1 < t2)
1187 self.assertTrue(t2 > t1)
1188 self.assertTrue(t1 <= t2)
1189 self.assertTrue(t2 >= t1)
1190 self.assertTrue(t1 != t2)
1191 self.assertTrue(t2 != t1)
1192 self.assertTrue(not t1 == t2)
1193 self.assertTrue(not t2 == t1)
1194 self.assertTrue(not t1 > t2)
1195 self.assertTrue(not t2 < t1)
1196 self.assertTrue(not t1 >= t2)
1197 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001198
Tim Peters68124bb2003-02-08 03:46:31 +00001199 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001200 self.assertEqual(t1 == badarg, False)
1201 self.assertEqual(t1 != badarg, True)
1202 self.assertEqual(badarg == t1, False)
1203 self.assertEqual(badarg != t1, True)
1204
Tim Peters2a799bf2002-12-16 20:18:38 +00001205 self.assertRaises(TypeError, lambda: t1 < badarg)
1206 self.assertRaises(TypeError, lambda: t1 > badarg)
1207 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001208 self.assertRaises(TypeError, lambda: badarg <= t1)
1209 self.assertRaises(TypeError, lambda: badarg < t1)
1210 self.assertRaises(TypeError, lambda: badarg > t1)
1211 self.assertRaises(TypeError, lambda: badarg >= t1)
1212
Tim Peters8d81a012003-01-24 22:36:34 +00001213 def test_mixed_compare(self):
1214 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001215
1216 # Our class can be compared for equality to other classes
1217 self.assertEqual(our == 1, False)
1218 self.assertEqual(1 == our, False)
1219 self.assertEqual(our != 1, True)
1220 self.assertEqual(1 != our, True)
1221
1222 # But the ordering is undefined
1223 self.assertRaises(TypeError, lambda: our < 1)
1224 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001225
Guido van Rossum19960592006-08-24 17:29:38 +00001226 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001227
Guido van Rossum19960592006-08-24 17:29:38 +00001228 class SomeClass:
1229 pass
1230
1231 their = SomeClass()
1232 self.assertEqual(our == their, False)
1233 self.assertEqual(their == our, False)
1234 self.assertEqual(our != their, True)
1235 self.assertEqual(their != our, True)
1236 self.assertRaises(TypeError, lambda: our < their)
1237 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001238
Guido van Rossum19960592006-08-24 17:29:38 +00001239 # However, if the other class explicitly defines ordering
1240 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001241
Guido van Rossum19960592006-08-24 17:29:38 +00001242 class LargerThanAnything:
1243 def __lt__(self, other):
1244 return False
1245 def __le__(self, other):
1246 return isinstance(other, LargerThanAnything)
1247 def __eq__(self, other):
1248 return isinstance(other, LargerThanAnything)
1249 def __ne__(self, other):
1250 return not isinstance(other, LargerThanAnything)
1251 def __gt__(self, other):
1252 return not isinstance(other, LargerThanAnything)
1253 def __ge__(self, other):
1254 return True
1255
1256 their = LargerThanAnything()
1257 self.assertEqual(our == their, False)
1258 self.assertEqual(their == our, False)
1259 self.assertEqual(our != their, True)
1260 self.assertEqual(their != our, True)
1261 self.assertEqual(our < their, True)
1262 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001263
Tim Peters2a799bf2002-12-16 20:18:38 +00001264 def test_bool(self):
1265 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001266 self.assertTrue(self.theclass.min)
1267 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001268
Guido van Rossum04110fb2007-08-24 16:32:05 +00001269 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001270 # For nasty technical reasons, we can't handle years before 1900.
1271 cls = self.theclass
1272 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1273 for y in 1, 49, 51, 99, 100, 1000, 1899:
1274 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001275
1276 def test_replace(self):
1277 cls = self.theclass
1278 args = [1, 2, 3]
1279 base = cls(*args)
1280 self.assertEqual(base, base.replace())
1281
1282 i = 0
1283 for name, newval in (("year", 2),
1284 ("month", 3),
1285 ("day", 4)):
1286 newargs = args[:]
1287 newargs[i] = newval
1288 expected = cls(*newargs)
1289 got = base.replace(**{name: newval})
1290 self.assertEqual(expected, got)
1291 i += 1
1292
1293 # Out of bounds.
1294 base = cls(2000, 2, 29)
1295 self.assertRaises(ValueError, base.replace, year=2001)
1296
Tim Petersa98924a2003-05-17 05:55:19 +00001297 def test_subclass_date(self):
1298
1299 class C(self.theclass):
1300 theAnswer = 42
1301
1302 def __new__(cls, *args, **kws):
1303 temp = kws.copy()
1304 extra = temp.pop('extra')
1305 result = self.theclass.__new__(cls, *args, **temp)
1306 result.extra = extra
1307 return result
1308
1309 def newmeth(self, start):
1310 return start + self.year + self.month
1311
1312 args = 2003, 4, 14
1313
1314 dt1 = self.theclass(*args)
1315 dt2 = C(*args, **{'extra': 7})
1316
1317 self.assertEqual(dt2.__class__, C)
1318 self.assertEqual(dt2.theAnswer, 42)
1319 self.assertEqual(dt2.extra, 7)
1320 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1321 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1322
Tim Peters604c0132004-06-07 23:04:33 +00001323 def test_pickling_subclass_date(self):
1324
1325 args = 6, 7, 23
1326 orig = SubclassDate(*args)
1327 for pickler, unpickler, proto in pickle_choices:
1328 green = pickler.dumps(orig, proto)
1329 derived = unpickler.loads(green)
1330 self.assertEqual(orig, derived)
1331
Tim Peters3f606292004-03-21 23:38:41 +00001332 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001333 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001334 # This is a low-overhead backdoor. A user can (by intent or
1335 # mistake) pass a string directly, which (if it's the right length)
1336 # will get treated like a pickle, and bypass the normal sanity
1337 # checks in the constructor. This can create insane objects.
1338 # The constructor doesn't want to burn the time to validate all
1339 # fields, but does check the month field. This stops, e.g.,
1340 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001341 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001342 if not issubclass(self.theclass, datetime):
1343 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001344 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001345 self.assertRaises(TypeError, self.theclass,
1346 base[:2] + month_byte + base[3:])
1347 for ord_byte in range(1, 13):
1348 # This shouldn't blow up because of the month byte alone. If
1349 # the implementation changes to do more-careful checking, it may
1350 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001351 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001352
Tim Peters2a799bf2002-12-16 20:18:38 +00001353#############################################################################
1354# datetime tests
1355
Tim Peters604c0132004-06-07 23:04:33 +00001356class SubclassDatetime(datetime):
1357 sub_var = 1
1358
Tim Peters2a799bf2002-12-16 20:18:38 +00001359class TestDateTime(TestDate):
1360
1361 theclass = datetime
1362
1363 def test_basic_attributes(self):
1364 dt = self.theclass(2002, 3, 1, 12, 0)
1365 self.assertEqual(dt.year, 2002)
1366 self.assertEqual(dt.month, 3)
1367 self.assertEqual(dt.day, 1)
1368 self.assertEqual(dt.hour, 12)
1369 self.assertEqual(dt.minute, 0)
1370 self.assertEqual(dt.second, 0)
1371 self.assertEqual(dt.microsecond, 0)
1372
1373 def test_basic_attributes_nonzero(self):
1374 # Make sure all attributes are non-zero so bugs in
1375 # bit-shifting access show up.
1376 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1377 self.assertEqual(dt.year, 2002)
1378 self.assertEqual(dt.month, 3)
1379 self.assertEqual(dt.day, 1)
1380 self.assertEqual(dt.hour, 12)
1381 self.assertEqual(dt.minute, 59)
1382 self.assertEqual(dt.second, 59)
1383 self.assertEqual(dt.microsecond, 8000)
1384
1385 def test_roundtrip(self):
1386 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1387 self.theclass.now()):
1388 # Verify dt -> string -> datetime identity.
1389 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001390 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001391 s = s[9:]
1392 dt2 = eval(s)
1393 self.assertEqual(dt, dt2)
1394
1395 # Verify identity via reconstructing from pieces.
1396 dt2 = self.theclass(dt.year, dt.month, dt.day,
1397 dt.hour, dt.minute, dt.second,
1398 dt.microsecond)
1399 self.assertEqual(dt, dt2)
1400
1401 def test_isoformat(self):
1402 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1403 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1404 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1405 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001406 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001407 # str is ISO format with the separator forced to a blank.
1408 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1409
1410 t = self.theclass(2, 3, 2)
1411 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1412 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1413 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1414 # str is ISO format with the separator forced to a blank.
1415 self.assertEqual(str(t), "0002-03-02 00:00:00")
1416
Eric Smith1ba31142007-09-11 18:06:02 +00001417 def test_format(self):
1418 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001419 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001420
1421 # check that a derived class's __str__() gets called
1422 class A(self.theclass):
1423 def __str__(self):
1424 return 'A'
1425 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001426 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001427
1428 # check that a derived class's strftime gets called
1429 class B(self.theclass):
1430 def strftime(self, format_spec):
1431 return 'B'
1432 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001433 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001434
1435 for fmt in ["m:%m d:%d y:%y",
1436 "m:%m d:%d y:%y H:%H M:%M S:%S",
1437 "%z %Z",
1438 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001439 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1440 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1441 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001442
Tim Peters2a799bf2002-12-16 20:18:38 +00001443 def test_more_ctime(self):
1444 # Test fields that TestDate doesn't touch.
1445 import time
1446
1447 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1448 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1449 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1450 # out. The difference is that t.ctime() produces " 2" for the day,
1451 # but platform ctime() produces "02" for the day. According to
1452 # C99, t.ctime() is correct here.
1453 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1454
1455 # So test a case where that difference doesn't matter.
1456 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1457 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1458
1459 def test_tz_independent_comparing(self):
1460 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1461 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1462 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1463 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001464 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001465
1466 # Make sure comparison doesn't forget microseconds, and isn't done
1467 # via comparing a float timestamp (an IEEE double doesn't have enough
1468 # precision to span microsecond resolution across years 1 thru 9999,
1469 # so comparing via timestamp necessarily calls some distinct values
1470 # equal).
1471 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1472 us = timedelta(microseconds=1)
1473 dt2 = dt1 + us
1474 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001475 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001476
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001477 def test_strftime_with_bad_tzname_replace(self):
1478 # verify ok if tzinfo.tzname().replace() returns a non-string
1479 class MyTzInfo(FixedOffset):
1480 def tzname(self, dt):
1481 class MyStr(str):
1482 def replace(self, *args):
1483 return None
1484 return MyStr('name')
1485 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1486 self.assertRaises(TypeError, t.strftime, '%Z')
1487
Tim Peters2a799bf2002-12-16 20:18:38 +00001488 def test_bad_constructor_arguments(self):
1489 # bad years
1490 self.theclass(MINYEAR, 1, 1) # no exception
1491 self.theclass(MAXYEAR, 1, 1) # no exception
1492 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1493 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1494 # bad months
1495 self.theclass(2000, 1, 1) # no exception
1496 self.theclass(2000, 12, 1) # no exception
1497 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1498 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1499 # bad days
1500 self.theclass(2000, 2, 29) # no exception
1501 self.theclass(2004, 2, 29) # no exception
1502 self.theclass(2400, 2, 29) # no exception
1503 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1504 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1505 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1506 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1507 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1508 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1509 # bad hours
1510 self.theclass(2000, 1, 31, 0) # no exception
1511 self.theclass(2000, 1, 31, 23) # no exception
1512 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1513 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1514 # bad minutes
1515 self.theclass(2000, 1, 31, 23, 0) # no exception
1516 self.theclass(2000, 1, 31, 23, 59) # no exception
1517 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1518 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1519 # bad seconds
1520 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1521 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1522 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1523 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1524 # bad microseconds
1525 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1526 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1527 self.assertRaises(ValueError, self.theclass,
1528 2000, 1, 31, 23, 59, 59, -1)
1529 self.assertRaises(ValueError, self.theclass,
1530 2000, 1, 31, 23, 59, 59,
1531 1000000)
1532
1533 def test_hash_equality(self):
1534 d = self.theclass(2000, 12, 31, 23, 30, 17)
1535 e = self.theclass(2000, 12, 31, 23, 30, 17)
1536 self.assertEqual(d, e)
1537 self.assertEqual(hash(d), hash(e))
1538
1539 dic = {d: 1}
1540 dic[e] = 2
1541 self.assertEqual(len(dic), 1)
1542 self.assertEqual(dic[d], 2)
1543 self.assertEqual(dic[e], 2)
1544
1545 d = self.theclass(2001, 1, 1, 0, 5, 17)
1546 e = self.theclass(2001, 1, 1, 0, 5, 17)
1547 self.assertEqual(d, e)
1548 self.assertEqual(hash(d), hash(e))
1549
1550 dic = {d: 1}
1551 dic[e] = 2
1552 self.assertEqual(len(dic), 1)
1553 self.assertEqual(dic[d], 2)
1554 self.assertEqual(dic[e], 2)
1555
1556 def test_computations(self):
1557 a = self.theclass(2002, 1, 31)
1558 b = self.theclass(1956, 1, 31)
1559 diff = a-b
1560 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1561 self.assertEqual(diff.seconds, 0)
1562 self.assertEqual(diff.microseconds, 0)
1563 a = self.theclass(2002, 3, 2, 17, 6)
1564 millisec = timedelta(0, 0, 1000)
1565 hour = timedelta(0, 3600)
1566 day = timedelta(1)
1567 week = timedelta(7)
1568 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1569 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1570 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1571 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1572 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1573 self.assertEqual(a - hour, a + -hour)
1574 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1575 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1576 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1577 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1578 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1579 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1580 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1581 self.assertEqual((a + week) - a, week)
1582 self.assertEqual((a + day) - a, day)
1583 self.assertEqual((a + hour) - a, hour)
1584 self.assertEqual((a + millisec) - a, millisec)
1585 self.assertEqual((a - week) - a, -week)
1586 self.assertEqual((a - day) - a, -day)
1587 self.assertEqual((a - hour) - a, -hour)
1588 self.assertEqual((a - millisec) - a, -millisec)
1589 self.assertEqual(a - (a + week), -week)
1590 self.assertEqual(a - (a + day), -day)
1591 self.assertEqual(a - (a + hour), -hour)
1592 self.assertEqual(a - (a + millisec), -millisec)
1593 self.assertEqual(a - (a - week), week)
1594 self.assertEqual(a - (a - day), day)
1595 self.assertEqual(a - (a - hour), hour)
1596 self.assertEqual(a - (a - millisec), millisec)
1597 self.assertEqual(a + (week + day + hour + millisec),
1598 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1599 self.assertEqual(a + (week + day + hour + millisec),
1600 (((a + week) + day) + hour) + millisec)
1601 self.assertEqual(a - (week + day + hour + millisec),
1602 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1603 self.assertEqual(a - (week + day + hour + millisec),
1604 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001605 # Add/sub ints or floats should be illegal
1606 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001607 self.assertRaises(TypeError, lambda: a+i)
1608 self.assertRaises(TypeError, lambda: a-i)
1609 self.assertRaises(TypeError, lambda: i+a)
1610 self.assertRaises(TypeError, lambda: i-a)
1611
1612 # delta - datetime is senseless.
1613 self.assertRaises(TypeError, lambda: day - a)
1614 # mixing datetime and (delta or datetime) via * or // is senseless
1615 self.assertRaises(TypeError, lambda: day * a)
1616 self.assertRaises(TypeError, lambda: a * day)
1617 self.assertRaises(TypeError, lambda: day // a)
1618 self.assertRaises(TypeError, lambda: a // day)
1619 self.assertRaises(TypeError, lambda: a * a)
1620 self.assertRaises(TypeError, lambda: a // a)
1621 # datetime + datetime is senseless
1622 self.assertRaises(TypeError, lambda: a + a)
1623
1624 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001625 args = 6, 7, 23, 20, 59, 1, 64**2
1626 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001627 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001628 green = pickler.dumps(orig, proto)
1629 derived = unpickler.loads(green)
1630 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001631
Guido van Rossum275666f2003-02-07 21:49:01 +00001632 def test_more_pickling(self):
1633 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1634 s = pickle.dumps(a)
1635 b = pickle.loads(s)
1636 self.assertEqual(b.year, 2003)
1637 self.assertEqual(b.month, 2)
1638 self.assertEqual(b.day, 7)
1639
Tim Peters604c0132004-06-07 23:04:33 +00001640 def test_pickling_subclass_datetime(self):
1641 args = 6, 7, 23, 20, 59, 1, 64**2
1642 orig = SubclassDatetime(*args)
1643 for pickler, unpickler, proto in pickle_choices:
1644 green = pickler.dumps(orig, proto)
1645 derived = unpickler.loads(green)
1646 self.assertEqual(orig, derived)
1647
Tim Peters2a799bf2002-12-16 20:18:38 +00001648 def test_more_compare(self):
1649 # The test_compare() inherited from TestDate covers the error cases.
1650 # We just want to test lexicographic ordering on the members datetime
1651 # has that date lacks.
1652 args = [2000, 11, 29, 20, 58, 16, 999998]
1653 t1 = self.theclass(*args)
1654 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001655 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001656 self.assertTrue(t1 <= t2)
1657 self.assertTrue(t1 >= t2)
1658 self.assertTrue(not t1 != t2)
1659 self.assertTrue(not t1 < t2)
1660 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001661
1662 for i in range(len(args)):
1663 newargs = args[:]
1664 newargs[i] = args[i] + 1
1665 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001666 self.assertTrue(t1 < t2)
1667 self.assertTrue(t2 > t1)
1668 self.assertTrue(t1 <= t2)
1669 self.assertTrue(t2 >= t1)
1670 self.assertTrue(t1 != t2)
1671 self.assertTrue(t2 != t1)
1672 self.assertTrue(not t1 == t2)
1673 self.assertTrue(not t2 == t1)
1674 self.assertTrue(not t1 > t2)
1675 self.assertTrue(not t2 < t1)
1676 self.assertTrue(not t1 >= t2)
1677 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001678
1679
1680 # A helper for timestamp constructor tests.
1681 def verify_field_equality(self, expected, got):
1682 self.assertEqual(expected.tm_year, got.year)
1683 self.assertEqual(expected.tm_mon, got.month)
1684 self.assertEqual(expected.tm_mday, got.day)
1685 self.assertEqual(expected.tm_hour, got.hour)
1686 self.assertEqual(expected.tm_min, got.minute)
1687 self.assertEqual(expected.tm_sec, got.second)
1688
1689 def test_fromtimestamp(self):
1690 import time
1691
1692 ts = time.time()
1693 expected = time.localtime(ts)
1694 got = self.theclass.fromtimestamp(ts)
1695 self.verify_field_equality(expected, got)
1696
1697 def test_utcfromtimestamp(self):
1698 import time
1699
1700 ts = time.time()
1701 expected = time.gmtime(ts)
1702 got = self.theclass.utcfromtimestamp(ts)
1703 self.verify_field_equality(expected, got)
1704
Thomas Wouters477c8d52006-05-27 19:21:47 +00001705 def test_microsecond_rounding(self):
1706 # Test whether fromtimestamp "rounds up" floats that are less
1707 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001708 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1709 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001710
Tim Peters1b6f7a92004-06-20 02:50:16 +00001711 def test_insane_fromtimestamp(self):
1712 # It's possible that some platform maps time_t to double,
1713 # and that this test will fail there. This test should
1714 # exempt such platforms (provided they return reasonable
1715 # results!).
1716 for insane in -1e200, 1e200:
1717 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1718 insane)
1719
1720 def test_insane_utcfromtimestamp(self):
1721 # It's possible that some platform maps time_t to double,
1722 # and that this test will fail there. This test should
1723 # exempt such platforms (provided they return reasonable
1724 # results!).
1725 for insane in -1e200, 1e200:
1726 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1727 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001728 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001729 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001730 # The result is tz-dependent; at least test that this doesn't
1731 # fail (like it did before bug 1646728 was fixed).
1732 self.theclass.fromtimestamp(-1.05)
1733
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001734 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001735 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001736 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001737 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001738
Tim Peters2a799bf2002-12-16 20:18:38 +00001739 def test_utcnow(self):
1740 import time
1741
1742 # Call it a success if utcnow() and utcfromtimestamp() are within
1743 # a second of each other.
1744 tolerance = timedelta(seconds=1)
1745 for dummy in range(3):
1746 from_now = self.theclass.utcnow()
1747 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1748 if abs(from_timestamp - from_now) <= tolerance:
1749 break
1750 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001751 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001752
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001753 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001754 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001755
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001756 string = '2004-12-01 13:02:47.197'
1757 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001758 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001759 got = self.theclass.strptime(string, format)
1760 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001761 self.assertIs(type(expected), self.theclass)
1762 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001763
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001764 strptime = self.theclass.strptime
1765 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1766 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1767 # Only local timezone and UTC are supported
1768 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1769 (-_time.timezone, _time.tzname[0])):
1770 if tzseconds < 0:
1771 sign = '-'
1772 seconds = -tzseconds
1773 else:
1774 sign ='+'
1775 seconds = tzseconds
1776 hours, minutes = divmod(seconds//60, 60)
1777 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1778 dt = strptime(dtstr, "%z %Z")
1779 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1780 self.assertEqual(dt.tzname(), tzname)
1781 # Can produce inconsistent datetime
1782 dtstr, fmt = "+1234 UTC", "%z %Z"
1783 dt = strptime(dtstr, fmt)
1784 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1785 self.assertEqual(dt.tzname(), 'UTC')
1786 # yet will roundtrip
1787 self.assertEqual(dt.strftime(fmt), dtstr)
1788
1789 # Produce naive datetime if no %z is provided
1790 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1791
1792 with self.assertRaises(ValueError): strptime("-2400", "%z")
1793 with self.assertRaises(ValueError): strptime("-000", "%z")
1794
Tim Peters2a799bf2002-12-16 20:18:38 +00001795 def test_more_timetuple(self):
1796 # This tests fields beyond those tested by the TestDate.test_timetuple.
1797 t = self.theclass(2004, 12, 31, 6, 22, 33)
1798 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1799 self.assertEqual(t.timetuple(),
1800 (t.year, t.month, t.day,
1801 t.hour, t.minute, t.second,
1802 t.weekday(),
1803 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1804 -1))
1805 tt = t.timetuple()
1806 self.assertEqual(tt.tm_year, t.year)
1807 self.assertEqual(tt.tm_mon, t.month)
1808 self.assertEqual(tt.tm_mday, t.day)
1809 self.assertEqual(tt.tm_hour, t.hour)
1810 self.assertEqual(tt.tm_min, t.minute)
1811 self.assertEqual(tt.tm_sec, t.second)
1812 self.assertEqual(tt.tm_wday, t.weekday())
1813 self.assertEqual(tt.tm_yday, t.toordinal() -
1814 date(t.year, 1, 1).toordinal() + 1)
1815 self.assertEqual(tt.tm_isdst, -1)
1816
1817 def test_more_strftime(self):
1818 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001819 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1820 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1821 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001822
1823 def test_extract(self):
1824 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1825 self.assertEqual(dt.date(), date(2002, 3, 4))
1826 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1827
1828 def test_combine(self):
1829 d = date(2002, 3, 4)
1830 t = time(18, 45, 3, 1234)
1831 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1832 combine = self.theclass.combine
1833 dt = combine(d, t)
1834 self.assertEqual(dt, expected)
1835
1836 dt = combine(time=t, date=d)
1837 self.assertEqual(dt, expected)
1838
1839 self.assertEqual(d, dt.date())
1840 self.assertEqual(t, dt.time())
1841 self.assertEqual(dt, combine(dt.date(), dt.time()))
1842
1843 self.assertRaises(TypeError, combine) # need an arg
1844 self.assertRaises(TypeError, combine, d) # need two args
1845 self.assertRaises(TypeError, combine, t, d) # args reversed
1846 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1847 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1848
Tim Peters12bf3392002-12-24 05:41:27 +00001849 def test_replace(self):
1850 cls = self.theclass
1851 args = [1, 2, 3, 4, 5, 6, 7]
1852 base = cls(*args)
1853 self.assertEqual(base, base.replace())
1854
1855 i = 0
1856 for name, newval in (("year", 2),
1857 ("month", 3),
1858 ("day", 4),
1859 ("hour", 5),
1860 ("minute", 6),
1861 ("second", 7),
1862 ("microsecond", 8)):
1863 newargs = args[:]
1864 newargs[i] = newval
1865 expected = cls(*newargs)
1866 got = base.replace(**{name: newval})
1867 self.assertEqual(expected, got)
1868 i += 1
1869
1870 # Out of bounds.
1871 base = cls(2000, 2, 29)
1872 self.assertRaises(ValueError, base.replace, year=2001)
1873
Tim Peters80475bb2002-12-25 07:40:55 +00001874 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001875 # Pretty boring! The TZ test is more interesting here. astimezone()
1876 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001877 dt = self.theclass.now()
1878 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001879 self.assertRaises(TypeError, dt.astimezone) # not enough args
1880 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1881 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001882 self.assertRaises(ValueError, dt.astimezone, f) # naive
1883 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001884
Tim Peters52dcce22003-01-23 16:36:11 +00001885 class Bogus(tzinfo):
1886 def utcoffset(self, dt): return None
1887 def dst(self, dt): return timedelta(0)
1888 bog = Bogus()
1889 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1890
1891 class AlsoBogus(tzinfo):
1892 def utcoffset(self, dt): return timedelta(0)
1893 def dst(self, dt): return None
1894 alsobog = AlsoBogus()
1895 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001896
Tim Petersa98924a2003-05-17 05:55:19 +00001897 def test_subclass_datetime(self):
1898
1899 class C(self.theclass):
1900 theAnswer = 42
1901
1902 def __new__(cls, *args, **kws):
1903 temp = kws.copy()
1904 extra = temp.pop('extra')
1905 result = self.theclass.__new__(cls, *args, **temp)
1906 result.extra = extra
1907 return result
1908
1909 def newmeth(self, start):
1910 return start + self.year + self.month + self.second
1911
1912 args = 2003, 4, 14, 12, 13, 41
1913
1914 dt1 = self.theclass(*args)
1915 dt2 = C(*args, **{'extra': 7})
1916
1917 self.assertEqual(dt2.__class__, C)
1918 self.assertEqual(dt2.theAnswer, 42)
1919 self.assertEqual(dt2.extra, 7)
1920 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1921 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1922 dt1.second - 7)
1923
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001924class TestSubclassDateTime(TestDateTime):
1925 theclass = SubclassDatetime
1926 # Override tests not designed for subclass
1927 def test_roundtrip(self):
1928 pass
1929
Tim Peters604c0132004-06-07 23:04:33 +00001930class SubclassTime(time):
1931 sub_var = 1
1932
Guido van Rossumd8faa362007-04-27 19:54:29 +00001933class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001934
1935 theclass = time
1936
1937 def test_basic_attributes(self):
1938 t = self.theclass(12, 0)
1939 self.assertEqual(t.hour, 12)
1940 self.assertEqual(t.minute, 0)
1941 self.assertEqual(t.second, 0)
1942 self.assertEqual(t.microsecond, 0)
1943
1944 def test_basic_attributes_nonzero(self):
1945 # Make sure all attributes are non-zero so bugs in
1946 # bit-shifting access show up.
1947 t = self.theclass(12, 59, 59, 8000)
1948 self.assertEqual(t.hour, 12)
1949 self.assertEqual(t.minute, 59)
1950 self.assertEqual(t.second, 59)
1951 self.assertEqual(t.microsecond, 8000)
1952
1953 def test_roundtrip(self):
1954 t = self.theclass(1, 2, 3, 4)
1955
1956 # Verify t -> string -> time identity.
1957 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001958 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001959 s = s[9:]
1960 t2 = eval(s)
1961 self.assertEqual(t, t2)
1962
1963 # Verify identity via reconstructing from pieces.
1964 t2 = self.theclass(t.hour, t.minute, t.second,
1965 t.microsecond)
1966 self.assertEqual(t, t2)
1967
1968 def test_comparing(self):
1969 args = [1, 2, 3, 4]
1970 t1 = self.theclass(*args)
1971 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001972 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001973 self.assertTrue(t1 <= t2)
1974 self.assertTrue(t1 >= t2)
1975 self.assertTrue(not t1 != t2)
1976 self.assertTrue(not t1 < t2)
1977 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001978
1979 for i in range(len(args)):
1980 newargs = args[:]
1981 newargs[i] = args[i] + 1
1982 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001983 self.assertTrue(t1 < t2)
1984 self.assertTrue(t2 > t1)
1985 self.assertTrue(t1 <= t2)
1986 self.assertTrue(t2 >= t1)
1987 self.assertTrue(t1 != t2)
1988 self.assertTrue(t2 != t1)
1989 self.assertTrue(not t1 == t2)
1990 self.assertTrue(not t2 == t1)
1991 self.assertTrue(not t1 > t2)
1992 self.assertTrue(not t2 < t1)
1993 self.assertTrue(not t1 >= t2)
1994 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001995
Tim Peters68124bb2003-02-08 03:46:31 +00001996 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001997 self.assertEqual(t1 == badarg, False)
1998 self.assertEqual(t1 != badarg, True)
1999 self.assertEqual(badarg == t1, False)
2000 self.assertEqual(badarg != t1, True)
2001
Tim Peters2a799bf2002-12-16 20:18:38 +00002002 self.assertRaises(TypeError, lambda: t1 <= badarg)
2003 self.assertRaises(TypeError, lambda: t1 < badarg)
2004 self.assertRaises(TypeError, lambda: t1 > badarg)
2005 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00002006 self.assertRaises(TypeError, lambda: badarg <= t1)
2007 self.assertRaises(TypeError, lambda: badarg < t1)
2008 self.assertRaises(TypeError, lambda: badarg > t1)
2009 self.assertRaises(TypeError, lambda: badarg >= t1)
2010
2011 def test_bad_constructor_arguments(self):
2012 # bad hours
2013 self.theclass(0, 0) # no exception
2014 self.theclass(23, 0) # no exception
2015 self.assertRaises(ValueError, self.theclass, -1, 0)
2016 self.assertRaises(ValueError, self.theclass, 24, 0)
2017 # bad minutes
2018 self.theclass(23, 0) # no exception
2019 self.theclass(23, 59) # no exception
2020 self.assertRaises(ValueError, self.theclass, 23, -1)
2021 self.assertRaises(ValueError, self.theclass, 23, 60)
2022 # bad seconds
2023 self.theclass(23, 59, 0) # no exception
2024 self.theclass(23, 59, 59) # no exception
2025 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2026 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2027 # bad microseconds
2028 self.theclass(23, 59, 59, 0) # no exception
2029 self.theclass(23, 59, 59, 999999) # no exception
2030 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2031 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2032
2033 def test_hash_equality(self):
2034 d = self.theclass(23, 30, 17)
2035 e = self.theclass(23, 30, 17)
2036 self.assertEqual(d, e)
2037 self.assertEqual(hash(d), hash(e))
2038
2039 dic = {d: 1}
2040 dic[e] = 2
2041 self.assertEqual(len(dic), 1)
2042 self.assertEqual(dic[d], 2)
2043 self.assertEqual(dic[e], 2)
2044
2045 d = self.theclass(0, 5, 17)
2046 e = self.theclass(0, 5, 17)
2047 self.assertEqual(d, e)
2048 self.assertEqual(hash(d), hash(e))
2049
2050 dic = {d: 1}
2051 dic[e] = 2
2052 self.assertEqual(len(dic), 1)
2053 self.assertEqual(dic[d], 2)
2054 self.assertEqual(dic[e], 2)
2055
2056 def test_isoformat(self):
2057 t = self.theclass(4, 5, 1, 123)
2058 self.assertEqual(t.isoformat(), "04:05:01.000123")
2059 self.assertEqual(t.isoformat(), str(t))
2060
2061 t = self.theclass()
2062 self.assertEqual(t.isoformat(), "00:00:00")
2063 self.assertEqual(t.isoformat(), str(t))
2064
2065 t = self.theclass(microsecond=1)
2066 self.assertEqual(t.isoformat(), "00:00:00.000001")
2067 self.assertEqual(t.isoformat(), str(t))
2068
2069 t = self.theclass(microsecond=10)
2070 self.assertEqual(t.isoformat(), "00:00:00.000010")
2071 self.assertEqual(t.isoformat(), str(t))
2072
2073 t = self.theclass(microsecond=100)
2074 self.assertEqual(t.isoformat(), "00:00:00.000100")
2075 self.assertEqual(t.isoformat(), str(t))
2076
2077 t = self.theclass(microsecond=1000)
2078 self.assertEqual(t.isoformat(), "00:00:00.001000")
2079 self.assertEqual(t.isoformat(), str(t))
2080
2081 t = self.theclass(microsecond=10000)
2082 self.assertEqual(t.isoformat(), "00:00:00.010000")
2083 self.assertEqual(t.isoformat(), str(t))
2084
2085 t = self.theclass(microsecond=100000)
2086 self.assertEqual(t.isoformat(), "00:00:00.100000")
2087 self.assertEqual(t.isoformat(), str(t))
2088
Thomas Wouterscf297e42007-02-23 15:07:44 +00002089 def test_1653736(self):
2090 # verify it doesn't accept extra keyword arguments
2091 t = self.theclass(second=1)
2092 self.assertRaises(TypeError, t.isoformat, foo=3)
2093
Tim Peters2a799bf2002-12-16 20:18:38 +00002094 def test_strftime(self):
2095 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002096 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002097 # A naive object replaces %z and %Z with empty strings.
2098 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2099
Eric Smith1ba31142007-09-11 18:06:02 +00002100 def test_format(self):
2101 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002102 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002103
2104 # check that a derived class's __str__() gets called
2105 class A(self.theclass):
2106 def __str__(self):
2107 return 'A'
2108 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002109 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002110
2111 # check that a derived class's strftime gets called
2112 class B(self.theclass):
2113 def strftime(self, format_spec):
2114 return 'B'
2115 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002116 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002117
2118 for fmt in ['%H %M %S',
2119 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002120 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2121 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2122 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002123
Tim Peters2a799bf2002-12-16 20:18:38 +00002124 def test_str(self):
2125 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2126 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2127 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2128 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2129 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2130
2131 def test_repr(self):
2132 name = 'datetime.' + self.theclass.__name__
2133 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2134 "%s(1, 2, 3, 4)" % name)
2135 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2136 "%s(10, 2, 3, 4000)" % name)
2137 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2138 "%s(0, 2, 3, 400000)" % name)
2139 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2140 "%s(12, 2, 3)" % name)
2141 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2142 "%s(23, 15)" % name)
2143
2144 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002145 self.assertIsInstance(self.theclass.min, self.theclass)
2146 self.assertIsInstance(self.theclass.max, self.theclass)
2147 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002148 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002149
2150 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002151 args = 20, 59, 16, 64**2
2152 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002153 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002154 green = pickler.dumps(orig, proto)
2155 derived = unpickler.loads(green)
2156 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002157
Tim Peters604c0132004-06-07 23:04:33 +00002158 def test_pickling_subclass_time(self):
2159 args = 20, 59, 16, 64**2
2160 orig = SubclassTime(*args)
2161 for pickler, unpickler, proto in pickle_choices:
2162 green = pickler.dumps(orig, proto)
2163 derived = unpickler.loads(green)
2164 self.assertEqual(orig, derived)
2165
Tim Peters2a799bf2002-12-16 20:18:38 +00002166 def test_bool(self):
2167 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002168 self.assertTrue(cls(1))
2169 self.assertTrue(cls(0, 1))
2170 self.assertTrue(cls(0, 0, 1))
2171 self.assertTrue(cls(0, 0, 0, 1))
2172 self.assertTrue(not cls(0))
2173 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002174
Tim Peters12bf3392002-12-24 05:41:27 +00002175 def test_replace(self):
2176 cls = self.theclass
2177 args = [1, 2, 3, 4]
2178 base = cls(*args)
2179 self.assertEqual(base, base.replace())
2180
2181 i = 0
2182 for name, newval in (("hour", 5),
2183 ("minute", 6),
2184 ("second", 7),
2185 ("microsecond", 8)):
2186 newargs = args[:]
2187 newargs[i] = newval
2188 expected = cls(*newargs)
2189 got = base.replace(**{name: newval})
2190 self.assertEqual(expected, got)
2191 i += 1
2192
2193 # Out of bounds.
2194 base = cls(1)
2195 self.assertRaises(ValueError, base.replace, hour=24)
2196 self.assertRaises(ValueError, base.replace, minute=-1)
2197 self.assertRaises(ValueError, base.replace, second=100)
2198 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2199
Tim Petersa98924a2003-05-17 05:55:19 +00002200 def test_subclass_time(self):
2201
2202 class C(self.theclass):
2203 theAnswer = 42
2204
2205 def __new__(cls, *args, **kws):
2206 temp = kws.copy()
2207 extra = temp.pop('extra')
2208 result = self.theclass.__new__(cls, *args, **temp)
2209 result.extra = extra
2210 return result
2211
2212 def newmeth(self, start):
2213 return start + self.hour + self.second
2214
2215 args = 4, 5, 6
2216
2217 dt1 = self.theclass(*args)
2218 dt2 = C(*args, **{'extra': 7})
2219
2220 self.assertEqual(dt2.__class__, C)
2221 self.assertEqual(dt2.theAnswer, 42)
2222 self.assertEqual(dt2.extra, 7)
2223 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2224 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2225
Armin Rigof4afb212005-11-07 07:15:48 +00002226 def test_backdoor_resistance(self):
2227 # see TestDate.test_backdoor_resistance().
2228 base = '2:59.0'
2229 for hour_byte in ' ', '9', chr(24), '\xff':
2230 self.assertRaises(TypeError, self.theclass,
2231 hour_byte + base[1:])
2232
Tim Peters855fe882002-12-22 03:43:39 +00002233# A mixin for classes with a tzinfo= argument. Subclasses must define
2234# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002235# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002236class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002237
Tim Petersbad8ff02002-12-30 20:52:32 +00002238 def test_argument_passing(self):
2239 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002240 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002241 class introspective(tzinfo):
2242 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002243 def utcoffset(self, dt):
2244 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002245 dst = utcoffset
2246
2247 obj = cls(1, 2, 3, tzinfo=introspective())
2248
Tim Peters0bf60bd2003-01-08 20:40:01 +00002249 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002250 self.assertEqual(obj.tzname(), expected)
2251
Tim Peters0bf60bd2003-01-08 20:40:01 +00002252 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002253 self.assertEqual(obj.utcoffset(), expected)
2254 self.assertEqual(obj.dst(), expected)
2255
Tim Peters855fe882002-12-22 03:43:39 +00002256 def test_bad_tzinfo_classes(self):
2257 cls = self.theclass
2258 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002259
Tim Peters855fe882002-12-22 03:43:39 +00002260 class NiceTry(object):
2261 def __init__(self): pass
2262 def utcoffset(self, dt): pass
2263 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2264
2265 class BetterTry(tzinfo):
2266 def __init__(self): pass
2267 def utcoffset(self, dt): pass
2268 b = BetterTry()
2269 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002270 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002271
2272 def test_utc_offset_out_of_bounds(self):
2273 class Edgy(tzinfo):
2274 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002275 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002276 def utcoffset(self, dt):
2277 return self.offset
2278
2279 cls = self.theclass
2280 for offset, legit in ((-1440, False),
2281 (-1439, True),
2282 (1439, True),
2283 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002284 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002285 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002286 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002287 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002288 else:
2289 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002290 if legit:
2291 aofs = abs(offset)
2292 h, m = divmod(aofs, 60)
2293 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002294 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002295 t = t.timetz()
2296 self.assertEqual(str(t), "01:02:03" + tag)
2297 else:
2298 self.assertRaises(ValueError, str, t)
2299
2300 def test_tzinfo_classes(self):
2301 cls = self.theclass
2302 class C1(tzinfo):
2303 def utcoffset(self, dt): return None
2304 def dst(self, dt): return None
2305 def tzname(self, dt): return None
2306 for t in (cls(1, 1, 1),
2307 cls(1, 1, 1, tzinfo=None),
2308 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002309 self.assertTrue(t.utcoffset() is None)
2310 self.assertTrue(t.dst() is None)
2311 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002312
Tim Peters855fe882002-12-22 03:43:39 +00002313 class C3(tzinfo):
2314 def utcoffset(self, dt): return timedelta(minutes=-1439)
2315 def dst(self, dt): return timedelta(minutes=1439)
2316 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002317 t = cls(1, 1, 1, tzinfo=C3())
2318 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2319 self.assertEqual(t.dst(), timedelta(minutes=1439))
2320 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002321
2322 # Wrong types.
2323 class C4(tzinfo):
2324 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002325 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002326 def tzname(self, dt): return 0
2327 t = cls(1, 1, 1, tzinfo=C4())
2328 self.assertRaises(TypeError, t.utcoffset)
2329 self.assertRaises(TypeError, t.dst)
2330 self.assertRaises(TypeError, t.tzname)
2331
2332 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002333 class C6(tzinfo):
2334 def utcoffset(self, dt): return timedelta(hours=-24)
2335 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002336 t = cls(1, 1, 1, tzinfo=C6())
2337 self.assertRaises(ValueError, t.utcoffset)
2338 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002339
2340 # Not a whole number of minutes.
2341 class C7(tzinfo):
2342 def utcoffset(self, dt): return timedelta(seconds=61)
2343 def dst(self, dt): return timedelta(microseconds=-81)
2344 t = cls(1, 1, 1, tzinfo=C7())
2345 self.assertRaises(ValueError, t.utcoffset)
2346 self.assertRaises(ValueError, t.dst)
2347
Tim Peters4c0db782002-12-26 05:01:19 +00002348 def test_aware_compare(self):
2349 cls = self.theclass
2350
Tim Peters60c76e42002-12-27 00:41:11 +00002351 # Ensure that utcoffset() gets ignored if the comparands have
2352 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002353 class OperandDependentOffset(tzinfo):
2354 def utcoffset(self, t):
2355 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002356 # d0 and d1 equal after adjustment
2357 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002358 else:
Tim Peters397301e2003-01-02 21:28:08 +00002359 # d2 off in the weeds
2360 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002361
2362 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2363 d0 = base.replace(minute=3)
2364 d1 = base.replace(minute=9)
2365 d2 = base.replace(minute=11)
2366 for x in d0, d1, d2:
2367 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002368 for op in lt, le, gt, ge, eq, ne:
2369 got = op(x, y)
2370 expected = op(x.minute, y.minute)
2371 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002372
2373 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002374 # Note that a time can't actually have an operand-depedent offset,
2375 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2376 # so skip this test for time.
2377 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002378 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2379 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2380 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2381 for x in d0, d1, d2:
2382 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002383 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002384 if (x is d0 or x is d1) and (y is d0 or y is d1):
2385 expected = 0
2386 elif x is y is d2:
2387 expected = 0
2388 elif x is d2:
2389 expected = -1
2390 else:
2391 assert y is d2
2392 expected = 1
2393 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002394
Tim Peters855fe882002-12-22 03:43:39 +00002395
Tim Peters0bf60bd2003-01-08 20:40:01 +00002396# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002397class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002398 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002399
2400 def test_empty(self):
2401 t = self.theclass()
2402 self.assertEqual(t.hour, 0)
2403 self.assertEqual(t.minute, 0)
2404 self.assertEqual(t.second, 0)
2405 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002406 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002407
Tim Peters2a799bf2002-12-16 20:18:38 +00002408 def test_zones(self):
2409 est = FixedOffset(-300, "EST", 1)
2410 utc = FixedOffset(0, "UTC", -2)
2411 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002412 t1 = time( 7, 47, tzinfo=est)
2413 t2 = time(12, 47, tzinfo=utc)
2414 t3 = time(13, 47, tzinfo=met)
2415 t4 = time(microsecond=40)
2416 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002417
2418 self.assertEqual(t1.tzinfo, est)
2419 self.assertEqual(t2.tzinfo, utc)
2420 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002421 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002422 self.assertEqual(t5.tzinfo, utc)
2423
Tim Peters855fe882002-12-22 03:43:39 +00002424 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2425 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2426 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002427 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002428 self.assertRaises(TypeError, t1.utcoffset, "no args")
2429
2430 self.assertEqual(t1.tzname(), "EST")
2431 self.assertEqual(t2.tzname(), "UTC")
2432 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002433 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002434 self.assertRaises(TypeError, t1.tzname, "no args")
2435
Tim Peters855fe882002-12-22 03:43:39 +00002436 self.assertEqual(t1.dst(), timedelta(minutes=1))
2437 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2438 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002439 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002440 self.assertRaises(TypeError, t1.dst, "no args")
2441
2442 self.assertEqual(hash(t1), hash(t2))
2443 self.assertEqual(hash(t1), hash(t3))
2444 self.assertEqual(hash(t2), hash(t3))
2445
2446 self.assertEqual(t1, t2)
2447 self.assertEqual(t1, t3)
2448 self.assertEqual(t2, t3)
2449 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2450 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2451 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2452
2453 self.assertEqual(str(t1), "07:47:00-05:00")
2454 self.assertEqual(str(t2), "12:47:00+00:00")
2455 self.assertEqual(str(t3), "13:47:00+01:00")
2456 self.assertEqual(str(t4), "00:00:00.000040")
2457 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2458
2459 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2460 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2461 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2462 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2463 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2464
Tim Peters0bf60bd2003-01-08 20:40:01 +00002465 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002466 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2467 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2468 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2469 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2470 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2471
2472 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2473 "07:47:00 %Z=EST %z=-0500")
2474 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2475 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2476
2477 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002478 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002479 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2480 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2481
Tim Petersb92bb712002-12-21 17:44:07 +00002482 # Check that an invalid tzname result raises an exception.
2483 class Badtzname(tzinfo):
2484 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002485 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002486 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2487 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002488
2489 def test_hash_edge_cases(self):
2490 # Offsets that overflow a basic time.
2491 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2492 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2493 self.assertEqual(hash(t1), hash(t2))
2494
2495 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2496 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2497 self.assertEqual(hash(t1), hash(t2))
2498
Tim Peters2a799bf2002-12-16 20:18:38 +00002499 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002500 # Try one without a tzinfo.
2501 args = 20, 59, 16, 64**2
2502 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002503 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002504 green = pickler.dumps(orig, proto)
2505 derived = unpickler.loads(green)
2506 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002507
2508 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002509 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002510 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002511 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002512 green = pickler.dumps(orig, proto)
2513 derived = unpickler.loads(green)
2514 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002515 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002516 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2517 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002518
2519 def test_more_bool(self):
2520 # Test cases with non-None tzinfo.
2521 cls = self.theclass
2522
2523 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002524 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002525
2526 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002527 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002528
2529 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002530 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002531
2532 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002533 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002534
2535 # Mostly ensuring this doesn't overflow internally.
2536 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002537 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002538
2539 # But this should yield a value error -- the utcoffset is bogus.
2540 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2541 self.assertRaises(ValueError, lambda: bool(t))
2542
2543 # Likewise.
2544 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2545 self.assertRaises(ValueError, lambda: bool(t))
2546
Tim Peters12bf3392002-12-24 05:41:27 +00002547 def test_replace(self):
2548 cls = self.theclass
2549 z100 = FixedOffset(100, "+100")
2550 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2551 args = [1, 2, 3, 4, z100]
2552 base = cls(*args)
2553 self.assertEqual(base, base.replace())
2554
2555 i = 0
2556 for name, newval in (("hour", 5),
2557 ("minute", 6),
2558 ("second", 7),
2559 ("microsecond", 8),
2560 ("tzinfo", zm200)):
2561 newargs = args[:]
2562 newargs[i] = newval
2563 expected = cls(*newargs)
2564 got = base.replace(**{name: newval})
2565 self.assertEqual(expected, got)
2566 i += 1
2567
2568 # Ensure we can get rid of a tzinfo.
2569 self.assertEqual(base.tzname(), "+100")
2570 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002571 self.assertTrue(base2.tzinfo is None)
2572 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002573
2574 # Ensure we can add one.
2575 base3 = base2.replace(tzinfo=z100)
2576 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002577 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002578
2579 # Out of bounds.
2580 base = cls(1)
2581 self.assertRaises(ValueError, base.replace, hour=24)
2582 self.assertRaises(ValueError, base.replace, minute=-1)
2583 self.assertRaises(ValueError, base.replace, second=100)
2584 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2585
Tim Peters60c76e42002-12-27 00:41:11 +00002586 def test_mixed_compare(self):
2587 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002588 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002589 self.assertEqual(t1, t2)
2590 t2 = t2.replace(tzinfo=None)
2591 self.assertEqual(t1, t2)
2592 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2593 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002594 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2595 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002596
Tim Peters0bf60bd2003-01-08 20:40:01 +00002597 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002598 class Varies(tzinfo):
2599 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002600 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002601 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002602 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002603 return self.offset
2604
2605 v = Varies()
2606 t1 = t2.replace(tzinfo=v)
2607 t2 = t2.replace(tzinfo=v)
2608 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2609 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2610 self.assertEqual(t1, t2)
2611
2612 # But if they're not identical, it isn't ignored.
2613 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002614 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002615
Tim Petersa98924a2003-05-17 05:55:19 +00002616 def test_subclass_timetz(self):
2617
2618 class C(self.theclass):
2619 theAnswer = 42
2620
2621 def __new__(cls, *args, **kws):
2622 temp = kws.copy()
2623 extra = temp.pop('extra')
2624 result = self.theclass.__new__(cls, *args, **temp)
2625 result.extra = extra
2626 return result
2627
2628 def newmeth(self, start):
2629 return start + self.hour + self.second
2630
2631 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2632
2633 dt1 = self.theclass(*args)
2634 dt2 = C(*args, **{'extra': 7})
2635
2636 self.assertEqual(dt2.__class__, C)
2637 self.assertEqual(dt2.theAnswer, 42)
2638 self.assertEqual(dt2.extra, 7)
2639 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2640 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2641
Tim Peters4c0db782002-12-26 05:01:19 +00002642
Tim Peters0bf60bd2003-01-08 20:40:01 +00002643# Testing datetime objects with a non-None tzinfo.
2644
Guido van Rossumd8faa362007-04-27 19:54:29 +00002645class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002646 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002647
2648 def test_trivial(self):
2649 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2650 self.assertEqual(dt.year, 1)
2651 self.assertEqual(dt.month, 2)
2652 self.assertEqual(dt.day, 3)
2653 self.assertEqual(dt.hour, 4)
2654 self.assertEqual(dt.minute, 5)
2655 self.assertEqual(dt.second, 6)
2656 self.assertEqual(dt.microsecond, 7)
2657 self.assertEqual(dt.tzinfo, None)
2658
2659 def test_even_more_compare(self):
2660 # The test_compare() and test_more_compare() inherited from TestDate
2661 # and TestDateTime covered non-tzinfo cases.
2662
2663 # Smallest possible after UTC adjustment.
2664 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2665 # Largest possible after UTC adjustment.
2666 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2667 tzinfo=FixedOffset(-1439, ""))
2668
2669 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002670 self.assertTrue(t1 < t2)
2671 self.assertTrue(t1 != t2)
2672 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002673
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002674 self.assertEqual(t1, t1)
2675 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002676
2677 # Equal afer adjustment.
2678 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2679 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2680 self.assertEqual(t1, t2)
2681
2682 # Change t1 not to subtract a minute, and t1 should be larger.
2683 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002684 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002685
2686 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2687 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002688 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002689
2690 # Back to the original t1, but make seconds resolve it.
2691 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2692 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002693 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002694
2695 # Likewise, but make microseconds resolve it.
2696 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2697 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002698 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002699
2700 # Make t2 naive and it should fail.
2701 t2 = self.theclass.min
2702 self.assertRaises(TypeError, lambda: t1 == t2)
2703 self.assertEqual(t2, t2)
2704
2705 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2706 class Naive(tzinfo):
2707 def utcoffset(self, dt): return None
2708 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2709 self.assertRaises(TypeError, lambda: t1 == t2)
2710 self.assertEqual(t2, t2)
2711
2712 # OTOH, it's OK to compare two of these mixing the two ways of being
2713 # naive.
2714 t1 = self.theclass(5, 6, 7)
2715 self.assertEqual(t1, t2)
2716
2717 # Try a bogus uctoffset.
2718 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002719 def utcoffset(self, dt):
2720 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002721 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2722 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002723 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002724
Tim Peters2a799bf2002-12-16 20:18:38 +00002725 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002726 # Try one without a tzinfo.
2727 args = 6, 7, 23, 20, 59, 1, 64**2
2728 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002729 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002730 green = pickler.dumps(orig, proto)
2731 derived = unpickler.loads(green)
2732 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002733
2734 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002735 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002736 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002737 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002738 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002739 green = pickler.dumps(orig, proto)
2740 derived = unpickler.loads(green)
2741 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002742 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002743 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2744 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002745
2746 def test_extreme_hashes(self):
2747 # If an attempt is made to hash these via subtracting the offset
2748 # then hashing a datetime object, OverflowError results. The
2749 # Python implementation used to blow up here.
2750 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2751 hash(t)
2752 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2753 tzinfo=FixedOffset(-1439, ""))
2754 hash(t)
2755
2756 # OTOH, an OOB offset should blow up.
2757 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2758 self.assertRaises(ValueError, hash, t)
2759
2760 def test_zones(self):
2761 est = FixedOffset(-300, "EST")
2762 utc = FixedOffset(0, "UTC")
2763 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002764 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2765 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2766 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002767 self.assertEqual(t1.tzinfo, est)
2768 self.assertEqual(t2.tzinfo, utc)
2769 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002770 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2771 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2772 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002773 self.assertEqual(t1.tzname(), "EST")
2774 self.assertEqual(t2.tzname(), "UTC")
2775 self.assertEqual(t3.tzname(), "MET")
2776 self.assertEqual(hash(t1), hash(t2))
2777 self.assertEqual(hash(t1), hash(t3))
2778 self.assertEqual(hash(t2), hash(t3))
2779 self.assertEqual(t1, t2)
2780 self.assertEqual(t1, t3)
2781 self.assertEqual(t2, t3)
2782 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2783 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2784 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002785 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002786 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2787 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2788 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2789
2790 def test_combine(self):
2791 met = FixedOffset(60, "MET")
2792 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002793 tz = time(18, 45, 3, 1234, tzinfo=met)
2794 dt = datetime.combine(d, tz)
2795 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002796 tzinfo=met))
2797
2798 def test_extract(self):
2799 met = FixedOffset(60, "MET")
2800 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2801 self.assertEqual(dt.date(), date(2002, 3, 4))
2802 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002803 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002804
2805 def test_tz_aware_arithmetic(self):
2806 import random
2807
2808 now = self.theclass.now()
2809 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002810 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002811 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002812 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002813 self.assertEqual(nowaware.timetz(), timeaware)
2814
2815 # Can't mix aware and non-aware.
2816 self.assertRaises(TypeError, lambda: now - nowaware)
2817 self.assertRaises(TypeError, lambda: nowaware - now)
2818
Tim Peters0bf60bd2003-01-08 20:40:01 +00002819 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002820 self.assertRaises(TypeError, lambda: now + nowaware)
2821 self.assertRaises(TypeError, lambda: nowaware + now)
2822 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2823
2824 # Subtracting should yield 0.
2825 self.assertEqual(now - now, timedelta(0))
2826 self.assertEqual(nowaware - nowaware, timedelta(0))
2827
2828 # Adding a delta should preserve tzinfo.
2829 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2830 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002831 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002832 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002833 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002834 self.assertEqual(nowawareplus, nowawareplus2)
2835
2836 # that - delta should be what we started with, and that - what we
2837 # started with should be delta.
2838 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002839 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002840 self.assertEqual(nowaware, diff)
2841 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2842 self.assertEqual(nowawareplus - nowaware, delta)
2843
2844 # Make up a random timezone.
2845 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002846 # Attach it to nowawareplus.
2847 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002848 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002849 # Make sure the difference takes the timezone adjustments into account.
2850 got = nowaware - nowawareplus
2851 # Expected: (nowaware base - nowaware offset) -
2852 # (nowawareplus base - nowawareplus offset) =
2853 # (nowaware base - nowawareplus base) +
2854 # (nowawareplus offset - nowaware offset) =
2855 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002856 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002857 self.assertEqual(got, expected)
2858
2859 # Try max possible difference.
2860 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2861 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2862 tzinfo=FixedOffset(-1439, "max"))
2863 maxdiff = max - min
2864 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2865 timedelta(minutes=2*1439))
2866
2867 def test_tzinfo_now(self):
2868 meth = self.theclass.now
2869 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2870 base = meth()
2871 # Try with and without naming the keyword.
2872 off42 = FixedOffset(42, "42")
2873 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002874 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002875 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002876 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002877 # Bad argument with and w/o naming the keyword.
2878 self.assertRaises(TypeError, meth, 16)
2879 self.assertRaises(TypeError, meth, tzinfo=16)
2880 # Bad keyword name.
2881 self.assertRaises(TypeError, meth, tinfo=off42)
2882 # Too many args.
2883 self.assertRaises(TypeError, meth, off42, off42)
2884
Tim Peters10cadce2003-01-23 19:58:02 +00002885 # We don't know which time zone we're in, and don't have a tzinfo
2886 # class to represent it, so seeing whether a tz argument actually
2887 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002888 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002889 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2890 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2891 for dummy in range(3):
2892 now = datetime.now(weirdtz)
2893 self.assertTrue(now.tzinfo is weirdtz)
2894 utcnow = datetime.utcnow().replace(tzinfo=utc)
2895 now2 = utcnow.astimezone(weirdtz)
2896 if abs(now - now2) < timedelta(seconds=30):
2897 break
2898 # Else the code is broken, or more than 30 seconds passed between
2899 # calls; assuming the latter, just try again.
2900 else:
2901 # Three strikes and we're out.
2902 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002903
Tim Peters2a799bf2002-12-16 20:18:38 +00002904 def test_tzinfo_fromtimestamp(self):
2905 import time
2906 meth = self.theclass.fromtimestamp
2907 ts = time.time()
2908 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2909 base = meth(ts)
2910 # Try with and without naming the keyword.
2911 off42 = FixedOffset(42, "42")
2912 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002913 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002914 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002915 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002916 # Bad argument with and w/o naming the keyword.
2917 self.assertRaises(TypeError, meth, ts, 16)
2918 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2919 # Bad keyword name.
2920 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2921 # Too many args.
2922 self.assertRaises(TypeError, meth, ts, off42, off42)
2923 # Too few args.
2924 self.assertRaises(TypeError, meth)
2925
Tim Peters2a44a8d2003-01-23 20:53:10 +00002926 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002927 timestamp = 1000000000
2928 utcdatetime = datetime.utcfromtimestamp(timestamp)
2929 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2930 # But on some flavor of Mac, it's nowhere near that. So we can't have
2931 # any idea here what time that actually is, we can only test that
2932 # relative changes match.
2933 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2934 tz = FixedOffset(utcoffset, "tz", 0)
2935 expected = utcdatetime + utcoffset
2936 got = datetime.fromtimestamp(timestamp, tz)
2937 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002938
Tim Peters2a799bf2002-12-16 20:18:38 +00002939 def test_tzinfo_utcnow(self):
2940 meth = self.theclass.utcnow
2941 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2942 base = meth()
2943 # Try with and without naming the keyword; for whatever reason,
2944 # utcnow() doesn't accept a tzinfo argument.
2945 off42 = FixedOffset(42, "42")
2946 self.assertRaises(TypeError, meth, off42)
2947 self.assertRaises(TypeError, meth, tzinfo=off42)
2948
2949 def test_tzinfo_utcfromtimestamp(self):
2950 import time
2951 meth = self.theclass.utcfromtimestamp
2952 ts = time.time()
2953 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2954 base = meth(ts)
2955 # Try with and without naming the keyword; for whatever reason,
2956 # utcfromtimestamp() doesn't accept a tzinfo argument.
2957 off42 = FixedOffset(42, "42")
2958 self.assertRaises(TypeError, meth, ts, off42)
2959 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2960
2961 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002962 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002963 # DST flag.
2964 class DST(tzinfo):
2965 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002966 if isinstance(dstvalue, int):
2967 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002968 self.dstvalue = dstvalue
2969 def dst(self, dt):
2970 return self.dstvalue
2971
2972 cls = self.theclass
2973 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2974 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2975 t = d.timetuple()
2976 self.assertEqual(1, t.tm_year)
2977 self.assertEqual(1, t.tm_mon)
2978 self.assertEqual(1, t.tm_mday)
2979 self.assertEqual(10, t.tm_hour)
2980 self.assertEqual(20, t.tm_min)
2981 self.assertEqual(30, t.tm_sec)
2982 self.assertEqual(0, t.tm_wday)
2983 self.assertEqual(1, t.tm_yday)
2984 self.assertEqual(flag, t.tm_isdst)
2985
2986 # dst() returns wrong type.
2987 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2988
2989 # dst() at the edge.
2990 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2991 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2992
2993 # dst() out of range.
2994 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2995 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2996
2997 def test_utctimetuple(self):
2998 class DST(tzinfo):
2999 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00003000 if isinstance(dstvalue, int):
3001 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00003002 self.dstvalue = dstvalue
3003 def dst(self, dt):
3004 return self.dstvalue
3005
3006 cls = self.theclass
3007 # This can't work: DST didn't implement utcoffset.
3008 self.assertRaises(NotImplementedError,
3009 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3010
3011 class UOFS(DST):
3012 def __init__(self, uofs, dofs=None):
3013 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00003014 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00003015 def utcoffset(self, dt):
3016 return self.uofs
3017
Tim Peters2a799bf2002-12-16 20:18:38 +00003018 for dstvalue in -33, 33, 0, None:
3019 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3020 t = d.utctimetuple()
3021 self.assertEqual(d.year, t.tm_year)
3022 self.assertEqual(d.month, t.tm_mon)
3023 self.assertEqual(d.day, t.tm_mday)
3024 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3025 self.assertEqual(13, t.tm_min)
3026 self.assertEqual(d.second, t.tm_sec)
3027 self.assertEqual(d.weekday(), t.tm_wday)
3028 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3029 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003030 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3031 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003032 self.assertEqual(0, t.tm_isdst)
3033
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003034 # Check that utctimetuple() is the same as
3035 # astimezone(utc).timetuple()
3036 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3037 for tz in [timezone.min, timezone.utc, timezone.max]:
3038 dtz = d.replace(tzinfo=tz)
3039 self.assertEqual(dtz.utctimetuple()[:-1],
3040 dtz.astimezone(timezone.utc).timetuple()[:-1])
3041 # At the edges, UTC adjustment can produce years out-of-range
3042 # for a datetime object. Ensure that an OverflowError is
3043 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003044 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3045 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003046 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003047
3048 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3049 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003050 self.assertRaises(OverflowError, huge.utctimetuple)
3051 # More overflow cases
3052 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3053 self.assertRaises(OverflowError, tiny.utctimetuple)
3054 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3055 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003056
3057 def test_tzinfo_isoformat(self):
3058 zero = FixedOffset(0, "+00:00")
3059 plus = FixedOffset(220, "+03:40")
3060 minus = FixedOffset(-231, "-03:51")
3061 unknown = FixedOffset(None, "")
3062
3063 cls = self.theclass
3064 datestr = '0001-02-03'
3065 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003066 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003067 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3068 timestr = '04:05:59' + (us and '.987001' or '')
3069 ofsstr = ofs is not None and d.tzname() or ''
3070 tailstr = timestr + ofsstr
3071 iso = d.isoformat()
3072 self.assertEqual(iso, datestr + 'T' + tailstr)
3073 self.assertEqual(iso, d.isoformat('T'))
3074 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003075 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003076 self.assertEqual(str(d), datestr + ' ' + tailstr)
3077
Tim Peters12bf3392002-12-24 05:41:27 +00003078 def test_replace(self):
3079 cls = self.theclass
3080 z100 = FixedOffset(100, "+100")
3081 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3082 args = [1, 2, 3, 4, 5, 6, 7, z100]
3083 base = cls(*args)
3084 self.assertEqual(base, base.replace())
3085
3086 i = 0
3087 for name, newval in (("year", 2),
3088 ("month", 3),
3089 ("day", 4),
3090 ("hour", 5),
3091 ("minute", 6),
3092 ("second", 7),
3093 ("microsecond", 8),
3094 ("tzinfo", zm200)):
3095 newargs = args[:]
3096 newargs[i] = newval
3097 expected = cls(*newargs)
3098 got = base.replace(**{name: newval})
3099 self.assertEqual(expected, got)
3100 i += 1
3101
3102 # Ensure we can get rid of a tzinfo.
3103 self.assertEqual(base.tzname(), "+100")
3104 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003105 self.assertTrue(base2.tzinfo is None)
3106 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003107
3108 # Ensure we can add one.
3109 base3 = base2.replace(tzinfo=z100)
3110 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003111 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003112
3113 # Out of bounds.
3114 base = cls(2000, 2, 29)
3115 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003116
Tim Peters80475bb2002-12-25 07:40:55 +00003117 def test_more_astimezone(self):
3118 # The inherited test_astimezone covered some trivial and error cases.
3119 fnone = FixedOffset(None, "None")
3120 f44m = FixedOffset(44, "44")
3121 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3122
Tim Peters10cadce2003-01-23 19:58:02 +00003123 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003124 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003125 # Replacing with degenerate tzinfo raises an exception.
3126 self.assertRaises(ValueError, dt.astimezone, fnone)
3127 # Ditto with None tz.
3128 self.assertRaises(TypeError, dt.astimezone, None)
3129 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003130 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003131 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003132 self.assertEqual(x.date(), dt.date())
3133 self.assertEqual(x.time(), dt.time())
3134
3135 # Replacing with different tzinfo does adjust.
3136 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003137 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003138 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3139 expected = dt - dt.utcoffset() # in effect, convert to UTC
3140 expected += fm5h.utcoffset(dt) # and from there to local time
3141 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3142 self.assertEqual(got.date(), expected.date())
3143 self.assertEqual(got.time(), expected.time())
3144 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003145 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003146 self.assertEqual(got, expected)
3147
Tim Peters4c0db782002-12-26 05:01:19 +00003148 def test_aware_subtract(self):
3149 cls = self.theclass
3150
Tim Peters60c76e42002-12-27 00:41:11 +00003151 # Ensure that utcoffset() is ignored when the operands have the
3152 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003153 class OperandDependentOffset(tzinfo):
3154 def utcoffset(self, t):
3155 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003156 # d0 and d1 equal after adjustment
3157 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003158 else:
Tim Peters397301e2003-01-02 21:28:08 +00003159 # d2 off in the weeds
3160 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003161
3162 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3163 d0 = base.replace(minute=3)
3164 d1 = base.replace(minute=9)
3165 d2 = base.replace(minute=11)
3166 for x in d0, d1, d2:
3167 for y in d0, d1, d2:
3168 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003169 expected = timedelta(minutes=x.minute - y.minute)
3170 self.assertEqual(got, expected)
3171
3172 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3173 # ignored.
3174 base = cls(8, 9, 10, 11, 12, 13, 14)
3175 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3176 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3177 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3178 for x in d0, d1, d2:
3179 for y in d0, d1, d2:
3180 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003181 if (x is d0 or x is d1) and (y is d0 or y is d1):
3182 expected = timedelta(0)
3183 elif x is y is d2:
3184 expected = timedelta(0)
3185 elif x is d2:
3186 expected = timedelta(minutes=(11-59)-0)
3187 else:
3188 assert y is d2
3189 expected = timedelta(minutes=0-(11-59))
3190 self.assertEqual(got, expected)
3191
Tim Peters60c76e42002-12-27 00:41:11 +00003192 def test_mixed_compare(self):
3193 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003194 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003195 self.assertEqual(t1, t2)
3196 t2 = t2.replace(tzinfo=None)
3197 self.assertEqual(t1, t2)
3198 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3199 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003200 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3201 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003202
Tim Peters0bf60bd2003-01-08 20:40:01 +00003203 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003204 class Varies(tzinfo):
3205 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003206 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003207 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003208 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003209 return self.offset
3210
3211 v = Varies()
3212 t1 = t2.replace(tzinfo=v)
3213 t2 = t2.replace(tzinfo=v)
3214 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3215 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3216 self.assertEqual(t1, t2)
3217
3218 # But if they're not identical, it isn't ignored.
3219 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003220 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003221
Tim Petersa98924a2003-05-17 05:55:19 +00003222 def test_subclass_datetimetz(self):
3223
3224 class C(self.theclass):
3225 theAnswer = 42
3226
3227 def __new__(cls, *args, **kws):
3228 temp = kws.copy()
3229 extra = temp.pop('extra')
3230 result = self.theclass.__new__(cls, *args, **temp)
3231 result.extra = extra
3232 return result
3233
3234 def newmeth(self, start):
3235 return start + self.hour + self.year
3236
3237 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3238
3239 dt1 = self.theclass(*args)
3240 dt2 = C(*args, **{'extra': 7})
3241
3242 self.assertEqual(dt2.__class__, C)
3243 self.assertEqual(dt2.theAnswer, 42)
3244 self.assertEqual(dt2.extra, 7)
3245 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3246 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3247
Tim Peters621818b2002-12-29 23:44:49 +00003248# Pain to set up DST-aware tzinfo classes.
3249
3250def first_sunday_on_or_after(dt):
3251 days_to_go = 6 - dt.weekday()
3252 if days_to_go:
3253 dt += timedelta(days_to_go)
3254 return dt
3255
3256ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003257MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003258HOUR = timedelta(hours=1)
3259DAY = timedelta(days=1)
3260# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3261DSTSTART = datetime(1, 4, 1, 2)
3262# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003263# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3264# being standard time on that day, there is no spelling in local time of
3265# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3266DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003267
3268class USTimeZone(tzinfo):
3269
3270 def __init__(self, hours, reprname, stdname, dstname):
3271 self.stdoffset = timedelta(hours=hours)
3272 self.reprname = reprname
3273 self.stdname = stdname
3274 self.dstname = dstname
3275
3276 def __repr__(self):
3277 return self.reprname
3278
3279 def tzname(self, dt):
3280 if self.dst(dt):
3281 return self.dstname
3282 else:
3283 return self.stdname
3284
3285 def utcoffset(self, dt):
3286 return self.stdoffset + self.dst(dt)
3287
3288 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003289 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003290 # An exception instead may be sensible here, in one or more of
3291 # the cases.
3292 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003293 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003294
3295 # Find first Sunday in April.
3296 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3297 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3298
3299 # Find last Sunday in October.
3300 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3301 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3302
Tim Peters621818b2002-12-29 23:44:49 +00003303 # Can't compare naive to aware objects, so strip the timezone from
3304 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003305 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003306 return HOUR
3307 else:
3308 return ZERO
3309
Tim Peters521fc152002-12-31 17:36:56 +00003310Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3311Central = USTimeZone(-6, "Central", "CST", "CDT")
3312Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3313Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003314utc_real = FixedOffset(0, "UTC", 0)
3315# For better test coverage, we want another flavor of UTC that's west of
3316# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003317utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003318
3319class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003320 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003321 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003322 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003323
Tim Peters0bf60bd2003-01-08 20:40:01 +00003324 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003325
Tim Peters521fc152002-12-31 17:36:56 +00003326 # Check a time that's inside DST.
3327 def checkinside(self, dt, tz, utc, dston, dstoff):
3328 self.assertEqual(dt.dst(), HOUR)
3329
3330 # Conversion to our own timezone is always an identity.
3331 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003332
3333 asutc = dt.astimezone(utc)
3334 there_and_back = asutc.astimezone(tz)
3335
3336 # Conversion to UTC and back isn't always an identity here,
3337 # because there are redundant spellings (in local time) of
3338 # UTC time when DST begins: the clock jumps from 1:59:59
3339 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3340 # make sense then. The classes above treat 2:MM:SS as
3341 # daylight time then (it's "after 2am"), really an alias
3342 # for 1:MM:SS standard time. The latter form is what
3343 # conversion back from UTC produces.
3344 if dt.date() == dston.date() and dt.hour == 2:
3345 # We're in the redundant hour, and coming back from
3346 # UTC gives the 1:MM:SS standard-time spelling.
3347 self.assertEqual(there_and_back + HOUR, dt)
3348 # Although during was considered to be in daylight
3349 # time, there_and_back is not.
3350 self.assertEqual(there_and_back.dst(), ZERO)
3351 # They're the same times in UTC.
3352 self.assertEqual(there_and_back.astimezone(utc),
3353 dt.astimezone(utc))
3354 else:
3355 # We're not in the redundant hour.
3356 self.assertEqual(dt, there_and_back)
3357
Tim Peters327098a2003-01-20 22:54:38 +00003358 # Because we have a redundant spelling when DST begins, there is
3359 # (unforunately) an hour when DST ends that can't be spelled at all in
3360 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3361 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3362 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3363 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3364 # expressed in local time. Nevertheless, we want conversion back
3365 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003366 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003367 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003368 if dt.date() == dstoff.date() and dt.hour == 0:
3369 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003370 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003371 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3372 nexthour_utc += HOUR
3373 nexthour_tz = nexthour_utc.astimezone(tz)
3374 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003375 else:
Tim Peters327098a2003-01-20 22:54:38 +00003376 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003377
3378 # Check a time that's outside DST.
3379 def checkoutside(self, dt, tz, utc):
3380 self.assertEqual(dt.dst(), ZERO)
3381
3382 # Conversion to our own timezone is always an identity.
3383 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003384
3385 # Converting to UTC and back is an identity too.
3386 asutc = dt.astimezone(utc)
3387 there_and_back = asutc.astimezone(tz)
3388 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003389
Tim Peters1024bf82002-12-30 17:09:40 +00003390 def convert_between_tz_and_utc(self, tz, utc):
3391 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003392 # Because 1:MM on the day DST ends is taken as being standard time,
3393 # there is no spelling in tz for the last hour of daylight time.
3394 # For purposes of the test, the last hour of DST is 0:MM, which is
3395 # taken as being daylight time (and 1:MM is taken as being standard
3396 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003397 dstoff = self.dstoff.replace(tzinfo=tz)
3398 for delta in (timedelta(weeks=13),
3399 DAY,
3400 HOUR,
3401 timedelta(minutes=1),
3402 timedelta(microseconds=1)):
3403
Tim Peters521fc152002-12-31 17:36:56 +00003404 self.checkinside(dston, tz, utc, dston, dstoff)
3405 for during in dston + delta, dstoff - delta:
3406 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003407
Tim Peters521fc152002-12-31 17:36:56 +00003408 self.checkoutside(dstoff, tz, utc)
3409 for outside in dston - delta, dstoff + delta:
3410 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003411
Tim Peters621818b2002-12-29 23:44:49 +00003412 def test_easy(self):
3413 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003414 self.convert_between_tz_and_utc(Eastern, utc_real)
3415 self.convert_between_tz_and_utc(Pacific, utc_real)
3416 self.convert_between_tz_and_utc(Eastern, utc_fake)
3417 self.convert_between_tz_and_utc(Pacific, utc_fake)
3418 # The next is really dancing near the edge. It works because
3419 # Pacific and Eastern are far enough apart that their "problem
3420 # hours" don't overlap.
3421 self.convert_between_tz_and_utc(Eastern, Pacific)
3422 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003423 # OTOH, these fail! Don't enable them. The difficulty is that
3424 # the edge case tests assume that every hour is representable in
3425 # the "utc" class. This is always true for a fixed-offset tzinfo
3426 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3427 # For these adjacent DST-aware time zones, the range of time offsets
3428 # tested ends up creating hours in the one that aren't representable
3429 # in the other. For the same reason, we would see failures in the
3430 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3431 # offset deltas in convert_between_tz_and_utc().
3432 #
3433 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3434 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003435
Tim Petersf3615152003-01-01 21:51:37 +00003436 def test_tricky(self):
3437 # 22:00 on day before daylight starts.
3438 fourback = self.dston - timedelta(hours=4)
3439 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003440 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003441 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3442 # 2", we should get the 3 spelling.
3443 # If we plug 22:00 the day before into Eastern, it "looks like std
3444 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3445 # to 22:00 lands on 2:00, which makes no sense in local time (the
3446 # local clock jumps from 1 to 3). The point here is to make sure we
3447 # get the 3 spelling.
3448 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003449 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003450 self.assertEqual(expected, got)
3451
3452 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3453 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003454 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003455 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3456 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3457 # spelling.
3458 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003459 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003460 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003461
Tim Petersadf64202003-01-04 06:03:15 +00003462 # Now on the day DST ends, we want "repeat an hour" behavior.
3463 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3464 # EST 23:MM 0:MM 1:MM 2:MM
3465 # EDT 0:MM 1:MM 2:MM 3:MM
3466 # wall 0:MM 1:MM 1:MM 2:MM against these
3467 for utc in utc_real, utc_fake:
3468 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003469 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003470 # Convert that to UTC.
3471 first_std_hour -= tz.utcoffset(None)
3472 # Adjust for possibly fake UTC.
3473 asutc = first_std_hour + utc.utcoffset(None)
3474 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3475 # tz=Eastern.
3476 asutcbase = asutc.replace(tzinfo=utc)
3477 for tzhour in (0, 1, 1, 2):
3478 expectedbase = self.dstoff.replace(hour=tzhour)
3479 for minute in 0, 30, 59:
3480 expected = expectedbase.replace(minute=minute)
3481 asutc = asutcbase.replace(minute=minute)
3482 astz = asutc.astimezone(tz)
3483 self.assertEqual(astz.replace(tzinfo=None), expected)
3484 asutcbase += HOUR
3485
3486
Tim Peters710fb152003-01-02 19:35:54 +00003487 def test_bogus_dst(self):
3488 class ok(tzinfo):
3489 def utcoffset(self, dt): return HOUR
3490 def dst(self, dt): return HOUR
3491
3492 now = self.theclass.now().replace(tzinfo=utc_real)
3493 # Doesn't blow up.
3494 now.astimezone(ok())
3495
3496 # Does blow up.
3497 class notok(ok):
3498 def dst(self, dt): return None
3499 self.assertRaises(ValueError, now.astimezone, notok())
3500
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003501 # Sometimes blow up. In the following, tzinfo.dst()
3502 # implementation may return None or not Nonedepending on
3503 # whether DST is assumed to be in effect. In this situation,
3504 # a ValueError should be raised by astimezone().
3505 class tricky_notok(ok):
3506 def dst(self, dt):
3507 if dt.year == 2000:
3508 return None
3509 else:
3510 return 10*HOUR
3511 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3512 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3513
Tim Peters52dcce22003-01-23 16:36:11 +00003514 def test_fromutc(self):
3515 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3516 now = datetime.utcnow().replace(tzinfo=utc_real)
3517 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3518 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3519 enow = Eastern.fromutc(now) # doesn't blow up
3520 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3521 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3522 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3523
3524 # Always converts UTC to standard time.
3525 class FauxUSTimeZone(USTimeZone):
3526 def fromutc(self, dt):
3527 return dt + self.stdoffset
3528 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3529
3530 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3531 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3532 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3533
3534 # Check around DST start.
3535 start = self.dston.replace(hour=4, tzinfo=Eastern)
3536 fstart = start.replace(tzinfo=FEastern)
3537 for wall in 23, 0, 1, 3, 4, 5:
3538 expected = start.replace(hour=wall)
3539 if wall == 23:
3540 expected -= timedelta(days=1)
3541 got = Eastern.fromutc(start)
3542 self.assertEqual(expected, got)
3543
3544 expected = fstart + FEastern.stdoffset
3545 got = FEastern.fromutc(fstart)
3546 self.assertEqual(expected, got)
3547
3548 # Ensure astimezone() calls fromutc() too.
3549 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3550 self.assertEqual(expected, got)
3551
3552 start += HOUR
3553 fstart += HOUR
3554
3555 # Check around DST end.
3556 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3557 fstart = start.replace(tzinfo=FEastern)
3558 for wall in 0, 1, 1, 2, 3, 4:
3559 expected = start.replace(hour=wall)
3560 got = Eastern.fromutc(start)
3561 self.assertEqual(expected, got)
3562
3563 expected = fstart + FEastern.stdoffset
3564 got = FEastern.fromutc(fstart)
3565 self.assertEqual(expected, got)
3566
3567 # Ensure astimezone() calls fromutc() too.
3568 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3569 self.assertEqual(expected, got)
3570
3571 start += HOUR
3572 fstart += HOUR
3573
Tim Peters710fb152003-01-02 19:35:54 +00003574
Tim Peters528ca532004-09-16 01:30:50 +00003575#############################################################################
3576# oddballs
3577
3578class Oddballs(unittest.TestCase):
3579
3580 def test_bug_1028306(self):
3581 # Trying to compare a date to a datetime should act like a mixed-
3582 # type comparison, despite that datetime is a subclass of date.
3583 as_date = date.today()
3584 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003585 self.assertTrue(as_date != as_datetime)
3586 self.assertTrue(as_datetime != as_date)
3587 self.assertTrue(not as_date == as_datetime)
3588 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003589 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3590 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3591 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3592 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3593 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3594 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3595 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3596 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3597
3598 # Neverthelss, comparison should work with the base-class (date)
3599 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003600 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003601 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003602 as_different = as_datetime.replace(day= different_day)
3603 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003604
3605 # And date should compare with other subclasses of date. If a
3606 # subclass wants to stop this, it's up to the subclass to do so.
3607 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3608 self.assertEqual(as_date, date_sc)
3609 self.assertEqual(date_sc, as_date)
3610
3611 # Ditto for datetimes.
3612 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3613 as_date.day, 0, 0, 0)
3614 self.assertEqual(as_datetime, datetime_sc)
3615 self.assertEqual(datetime_sc, as_datetime)
3616
Tim Peters2a799bf2002-12-16 20:18:38 +00003617def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003618 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003619
3620if __name__ == "__main__":
3621 test_main()