blob: 5b415af2ba9667e0c78031a8baefb1a9d43e47c1 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Alexander Belopolskyd5442cd2010-05-26 20:00:12 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinson7c186e22010-04-20 22:32:49 +000010from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
Mark Dickinsona56c4672009-01-27 18:17:45 +000011
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000018from datetime import timezone
Tim Peters0bf60bd2003-01-08 20:40:01 +000019from datetime import date, datetime
Alexander Belopolskyca94f552010-06-17 18:30:34 +000020import time as _time
Tim Peters0bf60bd2003-01-08 20:40:01 +000021
Guido van Rossumbe6fe542007-07-19 23:55:34 +000022pickle_choices = [(pickle, pickle, proto) for proto in range(3)]
23assert len(pickle_choices) == 3
Guido van Rossum177e41a2003-01-30 22:06:23 +000024
Tim Peters68124bb2003-02-08 03:46:31 +000025# An arbitrary collection of objects of non-datetime types, for testing
26# mixed-type comparisons.
Mark Dickinson5c2db372009-12-05 20:28:34 +000027OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
Tim Peters0bf60bd2003-01-08 20:40:01 +000028
Tim Peters2a799bf2002-12-16 20:18:38 +000029
Alexander Belopolsky1790bc42010-05-31 17:33:47 +000030# XXX Copied from test_float.
31INF = float("inf")
32NAN = float("nan")
33
34# decorator for skipping tests on non-IEEE 754 platforms
35requires_IEEE_754 = unittest.skipUnless(
36 float.__getformat__("double").startswith("IEEE"),
37 "test requires IEEE 754 doubles")
38
39
Tim Peters2a799bf2002-12-16 20:18:38 +000040#############################################################################
41# module tests
42
43class TestModule(unittest.TestCase):
44
45 def test_constants(self):
46 import datetime
47 self.assertEqual(datetime.MINYEAR, 1)
48 self.assertEqual(datetime.MAXYEAR, 9999)
49
50#############################################################################
51# tzinfo tests
52
53class FixedOffset(tzinfo):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000054
Tim Peters2a799bf2002-12-16 20:18:38 +000055 def __init__(self, offset, name, dstoffset=42):
Tim Peters397301e2003-01-02 21:28:08 +000056 if isinstance(offset, int):
57 offset = timedelta(minutes=offset)
58 if isinstance(dstoffset, int):
59 dstoffset = timedelta(minutes=dstoffset)
Tim Peters2a799bf2002-12-16 20:18:38 +000060 self.__offset = offset
61 self.__name = name
62 self.__dstoffset = dstoffset
63 def __repr__(self):
64 return self.__name.lower()
65 def utcoffset(self, dt):
66 return self.__offset
67 def tzname(self, dt):
68 return self.__name
69 def dst(self, dt):
70 return self.__dstoffset
71
Tim Petersfb8472c2002-12-21 05:04:42 +000072class PicklableFixedOffset(FixedOffset):
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000073
Tim Petersfb8472c2002-12-21 05:04:42 +000074 def __init__(self, offset=None, name=None, dstoffset=None):
75 FixedOffset.__init__(self, offset, name, dstoffset)
76
Tim Peters2a799bf2002-12-16 20:18:38 +000077class TestTZInfo(unittest.TestCase):
78
79 def test_non_abstractness(self):
80 # In order to allow subclasses to get pickled, the C implementation
81 # wasn't able to get away with having __init__ raise
82 # NotImplementedError.
83 useless = tzinfo()
84 dt = datetime.max
85 self.assertRaises(NotImplementedError, useless.tzname, dt)
86 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
87 self.assertRaises(NotImplementedError, useless.dst, dt)
88
89 def test_subclass_must_override(self):
90 class NotEnough(tzinfo):
91 def __init__(self, offset, name):
92 self.__offset = offset
93 self.__name = name
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000094 self.assertTrue(issubclass(NotEnough, tzinfo))
Tim Peters2a799bf2002-12-16 20:18:38 +000095 ne = NotEnough(3, "NotByALongShot")
Ezio Melottie9615932010-01-24 19:26:24 +000096 self.assertIsInstance(ne, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +000097
98 dt = datetime.now()
99 self.assertRaises(NotImplementedError, ne.tzname, dt)
100 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
101 self.assertRaises(NotImplementedError, ne.dst, dt)
102
103 def test_normal(self):
104 fo = FixedOffset(3, "Three")
Ezio Melottie9615932010-01-24 19:26:24 +0000105 self.assertIsInstance(fo, tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000106 for dt in datetime.now(), None:
Tim Peters397301e2003-01-02 21:28:08 +0000107 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000108 self.assertEqual(fo.tzname(dt), "Three")
Tim Peters397301e2003-01-02 21:28:08 +0000109 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +0000110
111 def test_pickling_base(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000112 # There's no point to pickling tzinfo objects on their own (they
113 # carry no data), but they need to be picklable anyway else
114 # concrete subclasses can't be pickled.
115 orig = tzinfo.__new__(tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000116 self.assertTrue(type(orig) is tzinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000117 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000118 green = pickler.dumps(orig, proto)
119 derived = unpickler.loads(green)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000120 self.assertTrue(type(derived) is tzinfo)
Tim Peters2a799bf2002-12-16 20:18:38 +0000121
122 def test_pickling_subclass(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000123 # Make sure we can pickle/unpickle an instance of a subclass.
Tim Peters397301e2003-01-02 21:28:08 +0000124 offset = timedelta(minutes=-300)
125 orig = PicklableFixedOffset(offset, 'cookie')
Ezio Melottie9615932010-01-24 19:26:24 +0000126 self.assertIsInstance(orig, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000127 self.assertTrue(type(orig) is PicklableFixedOffset)
Tim Peters397301e2003-01-02 21:28:08 +0000128 self.assertEqual(orig.utcoffset(None), offset)
Tim Peters2a799bf2002-12-16 20:18:38 +0000129 self.assertEqual(orig.tzname(None), 'cookie')
Guido van Rossum177e41a2003-01-30 22:06:23 +0000130 for pickler, unpickler, proto in pickle_choices:
Tim Petersf2715e02003-02-19 02:35:07 +0000131 green = pickler.dumps(orig, proto)
132 derived = unpickler.loads(green)
Ezio Melottie9615932010-01-24 19:26:24 +0000133 self.assertIsInstance(derived, tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000134 self.assertTrue(type(derived) is PicklableFixedOffset)
Tim Petersf2715e02003-02-19 02:35:07 +0000135 self.assertEqual(derived.utcoffset(None), offset)
136 self.assertEqual(derived.tzname(None), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +0000137
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000138class TestTimeZone(unittest.TestCase):
139
140 def setUp(self):
141 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
142 self.EST = timezone(-timedelta(hours=5), 'EST')
143 self.DT = datetime(2010, 1, 1)
144
145 def test_str(self):
146 for tz in [self.ACDT, self.EST, timezone.utc,
147 timezone.min, timezone.max]:
148 self.assertEqual(str(tz), tz.tzname(None))
149
150 def test_class_members(self):
151 limit = timedelta(hours=23, minutes=59)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000152 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
153 self.assertEqual(timezone.min.utcoffset(None), -limit)
154 self.assertEqual(timezone.max.utcoffset(None), limit)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000155
156
157 def test_constructor(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000158 self.assertEqual(timezone.utc, timezone(timedelta(0)))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000159 # invalid offsets
160 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
161 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
162 self.assertRaises(ValueError, timezone, invalid)
163 self.assertRaises(ValueError, timezone, -invalid)
164
165 with self.assertRaises(TypeError): timezone(None)
166 with self.assertRaises(TypeError): timezone(42)
167 with self.assertRaises(TypeError): timezone(ZERO, None)
168 with self.assertRaises(TypeError): timezone(ZERO, 42)
169
170 def test_inheritance(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000171 self.assertIsInstance(timezone.utc, tzinfo)
172 self.assertIsInstance(self.EST, tzinfo)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000173
174 def test_utcoffset(self):
175 dummy = self.DT
176 for h in [0, 1.5, 12]:
177 offset = h * HOUR
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000178 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
179 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000180
181 with self.assertRaises(TypeError): self.EST.utcoffset('')
182 with self.assertRaises(TypeError): self.EST.utcoffset(5)
183
184
185 def test_dst(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000186 self.assertEqual(None, timezone.utc.dst(self.DT))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000187
188 with self.assertRaises(TypeError): self.EST.dst('')
189 with self.assertRaises(TypeError): self.EST.dst(5)
190
191 def test_tzname(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000192 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
193 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
194 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
195 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
196 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000197
198 with self.assertRaises(TypeError): self.EST.tzname('')
199 with self.assertRaises(TypeError): self.EST.tzname(5)
200
201 def test_fromutc(self):
202 with self.assertRaises(ValueError):
203 timezone.utc.fromutc(self.DT)
204 for tz in [self.EST, self.ACDT, Eastern]:
205 utctime = self.DT.replace(tzinfo=tz)
206 local = tz.fromutc(utctime)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000207 self.assertEqual(local - utctime, tz.utcoffset(local))
208 self.assertEqual(local,
209 self.DT.replace(tzinfo=timezone.utc))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000210
211 def test_comparison(self):
212 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
213 self.assertEqual(timezone(HOUR), timezone(HOUR))
214 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
215 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
216 self.assertIn(timezone(ZERO), {timezone(ZERO)})
217
218 def test_aware_datetime(self):
219 # test that timezone instances can be used by datetime
220 t = datetime(1, 1, 1)
221 for tz in [timezone.min, timezone.max, timezone.utc]:
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000222 self.assertEqual(tz.tzname(t),
223 t.replace(tzinfo=tz).tzname())
224 self.assertEqual(tz.utcoffset(t),
225 t.replace(tzinfo=tz).utcoffset())
226 self.assertEqual(tz.dst(t),
227 t.replace(tzinfo=tz).dst())
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000228
Tim Peters2a799bf2002-12-16 20:18:38 +0000229#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000230# Base clase for testing a particular aspect of timedelta, time, date and
231# datetime comparisons.
232
Guido van Rossumd8faa362007-04-27 19:54:29 +0000233class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000234 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
235
236 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
237 # legit constructor.
238
239 def test_harmless_mixed_comparison(self):
240 me = self.theclass(1, 1, 1)
241
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000242 self.assertFalse(me == ())
243 self.assertTrue(me != ())
244 self.assertFalse(() == me)
245 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000246
Benjamin Peterson577473f2010-01-19 00:09:57 +0000247 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000248 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000249
250 def test_harmful_mixed_comparison(self):
251 me = self.theclass(1, 1, 1)
252
253 self.assertRaises(TypeError, lambda: me < ())
254 self.assertRaises(TypeError, lambda: me <= ())
255 self.assertRaises(TypeError, lambda: me > ())
256 self.assertRaises(TypeError, lambda: me >= ())
257
258 self.assertRaises(TypeError, lambda: () < me)
259 self.assertRaises(TypeError, lambda: () <= me)
260 self.assertRaises(TypeError, lambda: () > me)
261 self.assertRaises(TypeError, lambda: () >= me)
262
Tim Peters07534a62003-02-07 22:50:28 +0000263#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000264# timedelta tests
265
Guido van Rossumd8faa362007-04-27 19:54:29 +0000266class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000267
268 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000269
270 def test_constructor(self):
271 eq = self.assertEqual
272 td = timedelta
273
274 # Check keyword args to constructor
275 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
276 milliseconds=0, microseconds=0))
277 eq(td(1), td(days=1))
278 eq(td(0, 1), td(seconds=1))
279 eq(td(0, 0, 1), td(microseconds=1))
280 eq(td(weeks=1), td(days=7))
281 eq(td(days=1), td(hours=24))
282 eq(td(hours=1), td(minutes=60))
283 eq(td(minutes=1), td(seconds=60))
284 eq(td(seconds=1), td(milliseconds=1000))
285 eq(td(milliseconds=1), td(microseconds=1000))
286
287 # Check float args to constructor
288 eq(td(weeks=1.0/7), td(days=1))
289 eq(td(days=1.0/24), td(hours=1))
290 eq(td(hours=1.0/60), td(minutes=1))
291 eq(td(minutes=1.0/60), td(seconds=1))
292 eq(td(seconds=0.001), td(milliseconds=1))
293 eq(td(milliseconds=0.001), td(microseconds=1))
294
295 def test_computations(self):
296 eq = self.assertEqual
297 td = timedelta
298
299 a = td(7) # One week
300 b = td(0, 60) # One minute
301 c = td(0, 0, 1000) # One millisecond
302 eq(a+b+c, td(7, 60, 1000))
303 eq(a-b, td(6, 24*3600 - 60))
304 eq(-a, td(-7))
305 eq(+a, td(7))
306 eq(-b, td(-1, 24*3600 - 60))
307 eq(-c, td(-1, 24*3600 - 1, 999000))
308 eq(abs(a), a)
309 eq(abs(-a), a)
310 eq(td(6, 24*3600), a)
311 eq(td(0, 0, 60*1000000), b)
312 eq(a*10, td(70))
313 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000314 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000315 eq(b*10, td(0, 600))
316 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000317 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000318 eq(c*10, td(0, 0, 10000))
319 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000320 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000321 eq(a*-1, -a)
322 eq(b*-2, -b-b)
323 eq(c*-2, -c+-c)
324 eq(b*(60*24), (b*60)*24)
325 eq(b*(60*24), (60*b)*24)
326 eq(c*1000, td(0, 1))
327 eq(1000*c, td(0, 1))
328 eq(a//7, td(1))
329 eq(b//10, td(0, 6))
330 eq(c//1000, td(0, 0, 1))
331 eq(a//10, td(0, 7*24*360))
332 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000333 eq(a/0.5, td(14))
334 eq(b/0.5, td(0, 120))
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))
340
341 # Multiplication by float
342 us = td(microseconds=1)
343 eq((3*us) * 0.5, 2*us)
344 eq((5*us) * 0.5, 2*us)
345 eq(0.5 * (3*us), 2*us)
346 eq(0.5 * (5*us), 2*us)
347 eq((-3*us) * 0.5, -2*us)
348 eq((-5*us) * 0.5, -2*us)
349
350 # Division by int and float
351 eq((3*us) / 2, 2*us)
352 eq((5*us) / 2, 2*us)
353 eq((-3*us) / 2.0, -2*us)
354 eq((-5*us) / 2.0, -2*us)
355 eq((3*us) / -2, -2*us)
356 eq((5*us) / -2, -2*us)
357 eq((3*us) / -2.0, -2*us)
358 eq((5*us) / -2.0, -2*us)
359 for i in range(-10, 10):
360 eq((i*us/3)//us, round(i/3))
361 for i in range(-10, 10):
362 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000363
364 def test_disallowed_computations(self):
365 a = timedelta(42)
366
Mark Dickinson5c2db372009-12-05 20:28:34 +0000367 # Add/sub ints or floats should be illegal
368 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000369 self.assertRaises(TypeError, lambda: a+i)
370 self.assertRaises(TypeError, lambda: a-i)
371 self.assertRaises(TypeError, lambda: i+a)
372 self.assertRaises(TypeError, lambda: i-a)
373
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000374 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000375 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000376 zero = 0
377 self.assertRaises(TypeError, lambda: zero // a)
378 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000379 self.assertRaises(ZeroDivisionError, lambda: a / zero)
380 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
381
382 @requires_IEEE_754
383 def test_disallowed_special(self):
384 a = timedelta(42)
385 self.assertRaises(ValueError, a.__mul__, NAN)
386 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000387
388 def test_basic_attributes(self):
389 days, seconds, us = 1, 7, 31
390 td = timedelta(days, seconds, us)
391 self.assertEqual(td.days, days)
392 self.assertEqual(td.seconds, seconds)
393 self.assertEqual(td.microseconds, us)
394
Antoine Pitroube6859d2009-11-25 23:02:32 +0000395 def test_total_seconds(self):
396 td = timedelta(days=365)
397 self.assertEqual(td.total_seconds(), 31536000.0)
398 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
399 td = timedelta(seconds=total_seconds)
400 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000401 # Issue8644: Test that td.total_seconds() has the same
402 # accuracy as td / timedelta(seconds=1).
403 for ms in [-1, -2, -123]:
404 td = timedelta(microseconds=ms)
405 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000406
Tim Peters2a799bf2002-12-16 20:18:38 +0000407 def test_carries(self):
408 t1 = timedelta(days=100,
409 weeks=-7,
410 hours=-24*(100-49),
411 minutes=-3,
412 seconds=12,
413 microseconds=(3*60 - 12) * 1e6 + 1)
414 t2 = timedelta(microseconds=1)
415 self.assertEqual(t1, t2)
416
417 def test_hash_equality(self):
418 t1 = timedelta(days=100,
419 weeks=-7,
420 hours=-24*(100-49),
421 minutes=-3,
422 seconds=12,
423 microseconds=(3*60 - 12) * 1000000)
424 t2 = timedelta()
425 self.assertEqual(hash(t1), hash(t2))
426
427 t1 += timedelta(weeks=7)
428 t2 += timedelta(days=7*7)
429 self.assertEqual(t1, t2)
430 self.assertEqual(hash(t1), hash(t2))
431
432 d = {t1: 1}
433 d[t2] = 2
434 self.assertEqual(len(d), 1)
435 self.assertEqual(d[t1], 2)
436
437 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000438 args = 12, 34, 56
439 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000440 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000441 green = pickler.dumps(orig, proto)
442 derived = unpickler.loads(green)
443 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000444
445 def test_compare(self):
446 t1 = timedelta(2, 3, 4)
447 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000448 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000449 self.assertTrue(t1 <= t2)
450 self.assertTrue(t1 >= t2)
451 self.assertTrue(not t1 != t2)
452 self.assertTrue(not t1 < t2)
453 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000454
455 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
456 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000457 self.assertTrue(t1 < t2)
458 self.assertTrue(t2 > t1)
459 self.assertTrue(t1 <= t2)
460 self.assertTrue(t2 >= t1)
461 self.assertTrue(t1 != t2)
462 self.assertTrue(t2 != t1)
463 self.assertTrue(not t1 == t2)
464 self.assertTrue(not t2 == t1)
465 self.assertTrue(not t1 > t2)
466 self.assertTrue(not t2 < t1)
467 self.assertTrue(not t1 >= t2)
468 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000469
Tim Peters68124bb2003-02-08 03:46:31 +0000470 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000471 self.assertEqual(t1 == badarg, False)
472 self.assertEqual(t1 != badarg, True)
473 self.assertEqual(badarg == t1, False)
474 self.assertEqual(badarg != t1, True)
475
Tim Peters2a799bf2002-12-16 20:18:38 +0000476 self.assertRaises(TypeError, lambda: t1 <= badarg)
477 self.assertRaises(TypeError, lambda: t1 < badarg)
478 self.assertRaises(TypeError, lambda: t1 > badarg)
479 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000480 self.assertRaises(TypeError, lambda: badarg <= t1)
481 self.assertRaises(TypeError, lambda: badarg < t1)
482 self.assertRaises(TypeError, lambda: badarg > t1)
483 self.assertRaises(TypeError, lambda: badarg >= t1)
484
485 def test_str(self):
486 td = timedelta
487 eq = self.assertEqual
488
489 eq(str(td(1)), "1 day, 0:00:00")
490 eq(str(td(-1)), "-1 day, 0:00:00")
491 eq(str(td(2)), "2 days, 0:00:00")
492 eq(str(td(-2)), "-2 days, 0:00:00")
493
494 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
495 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
496 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
497 "-210 days, 23:12:34")
498
499 eq(str(td(milliseconds=1)), "0:00:00.001000")
500 eq(str(td(microseconds=3)), "0:00:00.000003")
501
502 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
503 microseconds=999999)),
504 "999999999 days, 23:59:59.999999")
505
506 def test_roundtrip(self):
507 for td in (timedelta(days=999999999, hours=23, minutes=59,
508 seconds=59, microseconds=999999),
509 timedelta(days=-999999999),
510 timedelta(days=1, seconds=2, microseconds=3)):
511
512 # Verify td -> string -> td identity.
513 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000514 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000515 s = s[9:]
516 td2 = eval(s)
517 self.assertEqual(td, td2)
518
519 # Verify identity via reconstructing from pieces.
520 td2 = timedelta(td.days, td.seconds, td.microseconds)
521 self.assertEqual(td, td2)
522
523 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000524 self.assertIsInstance(timedelta.min, timedelta)
525 self.assertIsInstance(timedelta.max, timedelta)
526 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000527 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000528 self.assertEqual(timedelta.min, timedelta(-999999999))
529 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
530 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
531
532 def test_overflow(self):
533 tiny = timedelta.resolution
534
535 td = timedelta.min + tiny
536 td -= tiny # no problem
537 self.assertRaises(OverflowError, td.__sub__, tiny)
538 self.assertRaises(OverflowError, td.__add__, -tiny)
539
540 td = timedelta.max - tiny
541 td += tiny # no problem
542 self.assertRaises(OverflowError, td.__add__, tiny)
543 self.assertRaises(OverflowError, td.__sub__, -tiny)
544
545 self.assertRaises(OverflowError, lambda: -timedelta.max)
546
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000547 day = timedelta(1)
548 self.assertRaises(OverflowError, day.__mul__, 10**9)
549 self.assertRaises(OverflowError, day.__mul__, 1e9)
550 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
551 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
552 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
553
554 @requires_IEEE_754
555 def _test_overflow_special(self):
556 day = timedelta(1)
557 self.assertRaises(OverflowError, day.__mul__, INF)
558 self.assertRaises(OverflowError, day.__mul__, -INF)
559
Tim Peters2a799bf2002-12-16 20:18:38 +0000560 def test_microsecond_rounding(self):
561 td = timedelta
562 eq = self.assertEqual
563
564 # Single-field rounding.
565 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
566 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
567 eq(td(milliseconds=0.6/1000), td(microseconds=1))
568 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
569
570 # Rounding due to contributions from more than one field.
571 us_per_hour = 3600e6
572 us_per_day = us_per_hour * 24
573 eq(td(days=.4/us_per_day), td(0))
574 eq(td(hours=.2/us_per_hour), td(0))
575 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
576
577 eq(td(days=-.4/us_per_day), td(0))
578 eq(td(hours=-.2/us_per_hour), td(0))
579 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
580
581 def test_massive_normalization(self):
582 td = timedelta(microseconds=-1)
583 self.assertEqual((td.days, td.seconds, td.microseconds),
584 (-1, 24*3600-1, 999999))
585
586 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000587 self.assertTrue(timedelta(1))
588 self.assertTrue(timedelta(0, 1))
589 self.assertTrue(timedelta(0, 0, 1))
590 self.assertTrue(timedelta(microseconds=1))
591 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000592
Tim Petersb0c854d2003-05-17 15:57:00 +0000593 def test_subclass_timedelta(self):
594
595 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000596 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000597 def from_td(td):
598 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000599
600 def as_hours(self):
601 sum = (self.days * 24 +
602 self.seconds / 3600.0 +
603 self.microseconds / 3600e6)
604 return round(sum)
605
606 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000607 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000608 self.assertEqual(t1.as_hours(), 24)
609
610 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000611 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000612 self.assertEqual(t2.as_hours(), -25)
613
614 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000615 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000616 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000617 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000618 self.assertEqual(t3.days, t4.days)
619 self.assertEqual(t3.seconds, t4.seconds)
620 self.assertEqual(t3.microseconds, t4.microseconds)
621 self.assertEqual(str(t3), str(t4))
622 self.assertEqual(t4.as_hours(), -1)
623
Mark Dickinson7c186e22010-04-20 22:32:49 +0000624 def test_division(self):
625 t = timedelta(hours=1, minutes=24, seconds=19)
626 second = timedelta(seconds=1)
627 self.assertEqual(t / second, 5059.0)
628 self.assertEqual(t // second, 5059)
629
630 t = timedelta(minutes=2, seconds=30)
631 minute = timedelta(minutes=1)
632 self.assertEqual(t / minute, 2.5)
633 self.assertEqual(t // minute, 2)
634
635 zerotd = timedelta(0)
636 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
637 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
638
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000639 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000640 # note: floor division of a timedelta by an integer *is*
641 # currently permitted.
642
643 def test_remainder(self):
644 t = timedelta(minutes=2, seconds=30)
645 minute = timedelta(minutes=1)
646 r = t % minute
647 self.assertEqual(r, timedelta(seconds=30))
648
649 t = timedelta(minutes=-2, seconds=30)
650 r = t % minute
651 self.assertEqual(r, timedelta(seconds=30))
652
653 zerotd = timedelta(0)
654 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
655
656 self.assertRaises(TypeError, mod, t, 10)
657
658 def test_divmod(self):
659 t = timedelta(minutes=2, seconds=30)
660 minute = timedelta(minutes=1)
661 q, r = divmod(t, minute)
662 self.assertEqual(q, 2)
663 self.assertEqual(r, timedelta(seconds=30))
664
665 t = timedelta(minutes=-2, seconds=30)
666 q, r = divmod(t, minute)
667 self.assertEqual(q, -2)
668 self.assertEqual(r, timedelta(seconds=30))
669
670 zerotd = timedelta(0)
671 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
672
673 self.assertRaises(TypeError, divmod, t, 10)
674
675
Tim Peters2a799bf2002-12-16 20:18:38 +0000676#############################################################################
677# date tests
678
679class TestDateOnly(unittest.TestCase):
680 # Tests here won't pass if also run on datetime objects, so don't
681 # subclass this to test datetimes too.
682
683 def test_delta_non_days_ignored(self):
684 dt = date(2000, 1, 2)
685 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
686 microseconds=5)
687 days = timedelta(delta.days)
688 self.assertEqual(days, timedelta(1))
689
690 dt2 = dt + delta
691 self.assertEqual(dt2, dt + days)
692
693 dt2 = delta + dt
694 self.assertEqual(dt2, dt + days)
695
696 dt2 = dt - delta
697 self.assertEqual(dt2, dt - days)
698
699 delta = -delta
700 days = timedelta(delta.days)
701 self.assertEqual(days, timedelta(-2))
702
703 dt2 = dt + delta
704 self.assertEqual(dt2, dt + days)
705
706 dt2 = delta + dt
707 self.assertEqual(dt2, dt + days)
708
709 dt2 = dt - delta
710 self.assertEqual(dt2, dt - days)
711
Tim Peters604c0132004-06-07 23:04:33 +0000712class SubclassDate(date):
713 sub_var = 1
714
Guido van Rossumd8faa362007-04-27 19:54:29 +0000715class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000716 # Tests here should pass for both dates and datetimes, except for a
717 # few tests that TestDateTime overrides.
718
719 theclass = date
720
721 def test_basic_attributes(self):
722 dt = self.theclass(2002, 3, 1)
723 self.assertEqual(dt.year, 2002)
724 self.assertEqual(dt.month, 3)
725 self.assertEqual(dt.day, 1)
726
727 def test_roundtrip(self):
728 for dt in (self.theclass(1, 2, 3),
729 self.theclass.today()):
730 # Verify dt -> string -> date identity.
731 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000732 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000733 s = s[9:]
734 dt2 = eval(s)
735 self.assertEqual(dt, dt2)
736
737 # Verify identity via reconstructing from pieces.
738 dt2 = self.theclass(dt.year, dt.month, dt.day)
739 self.assertEqual(dt, dt2)
740
741 def test_ordinal_conversions(self):
742 # Check some fixed values.
743 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
744 (1, 12, 31, 365),
745 (2, 1, 1, 366),
746 # first example from "Calendrical Calculations"
747 (1945, 11, 12, 710347)]:
748 d = self.theclass(y, m, d)
749 self.assertEqual(n, d.toordinal())
750 fromord = self.theclass.fromordinal(n)
751 self.assertEqual(d, fromord)
752 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000753 # if we're checking something fancier than a date, verify
754 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000755 self.assertEqual(fromord.hour, 0)
756 self.assertEqual(fromord.minute, 0)
757 self.assertEqual(fromord.second, 0)
758 self.assertEqual(fromord.microsecond, 0)
759
Tim Peters0bf60bd2003-01-08 20:40:01 +0000760 # Check first and last days of year spottily across the whole
761 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000762 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000763 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
764 d = self.theclass(year, 1, 1)
765 n = d.toordinal()
766 d2 = self.theclass.fromordinal(n)
767 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000768 # Verify that moving back a day gets to the end of year-1.
769 if year > 1:
770 d = self.theclass.fromordinal(n-1)
771 d2 = self.theclass(year-1, 12, 31)
772 self.assertEqual(d, d2)
773 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000774
775 # Test every day in a leap-year and a non-leap year.
776 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
777 for year, isleap in (2000, True), (2002, False):
778 n = self.theclass(year, 1, 1).toordinal()
779 for month, maxday in zip(range(1, 13), dim):
780 if month == 2 and isleap:
781 maxday += 1
782 for day in range(1, maxday+1):
783 d = self.theclass(year, month, day)
784 self.assertEqual(d.toordinal(), n)
785 self.assertEqual(d, self.theclass.fromordinal(n))
786 n += 1
787
788 def test_extreme_ordinals(self):
789 a = self.theclass.min
790 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
791 aord = a.toordinal()
792 b = a.fromordinal(aord)
793 self.assertEqual(a, b)
794
795 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
796
797 b = a + timedelta(days=1)
798 self.assertEqual(b.toordinal(), aord + 1)
799 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
800
801 a = self.theclass.max
802 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
803 aord = a.toordinal()
804 b = a.fromordinal(aord)
805 self.assertEqual(a, b)
806
807 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
808
809 b = a - timedelta(days=1)
810 self.assertEqual(b.toordinal(), aord - 1)
811 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
812
813 def test_bad_constructor_arguments(self):
814 # bad years
815 self.theclass(MINYEAR, 1, 1) # no exception
816 self.theclass(MAXYEAR, 1, 1) # no exception
817 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
818 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
819 # bad months
820 self.theclass(2000, 1, 1) # no exception
821 self.theclass(2000, 12, 1) # no exception
822 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
823 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
824 # bad days
825 self.theclass(2000, 2, 29) # no exception
826 self.theclass(2004, 2, 29) # no exception
827 self.theclass(2400, 2, 29) # no exception
828 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
829 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
830 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
831 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
832 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
833 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
834
835 def test_hash_equality(self):
836 d = self.theclass(2000, 12, 31)
837 # same thing
838 e = self.theclass(2000, 12, 31)
839 self.assertEqual(d, e)
840 self.assertEqual(hash(d), hash(e))
841
842 dic = {d: 1}
843 dic[e] = 2
844 self.assertEqual(len(dic), 1)
845 self.assertEqual(dic[d], 2)
846 self.assertEqual(dic[e], 2)
847
848 d = self.theclass(2001, 1, 1)
849 # same thing
850 e = self.theclass(2001, 1, 1)
851 self.assertEqual(d, e)
852 self.assertEqual(hash(d), hash(e))
853
854 dic = {d: 1}
855 dic[e] = 2
856 self.assertEqual(len(dic), 1)
857 self.assertEqual(dic[d], 2)
858 self.assertEqual(dic[e], 2)
859
860 def test_computations(self):
861 a = self.theclass(2002, 1, 31)
862 b = self.theclass(1956, 1, 31)
863
864 diff = a-b
865 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
866 self.assertEqual(diff.seconds, 0)
867 self.assertEqual(diff.microseconds, 0)
868
869 day = timedelta(1)
870 week = timedelta(7)
871 a = self.theclass(2002, 3, 2)
872 self.assertEqual(a + day, self.theclass(2002, 3, 3))
873 self.assertEqual(day + a, self.theclass(2002, 3, 3))
874 self.assertEqual(a - day, self.theclass(2002, 3, 1))
875 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
876 self.assertEqual(a + week, self.theclass(2002, 3, 9))
877 self.assertEqual(a - week, self.theclass(2002, 2, 23))
878 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
879 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
880 self.assertEqual((a + week) - a, week)
881 self.assertEqual((a + day) - a, day)
882 self.assertEqual((a - week) - a, -week)
883 self.assertEqual((a - day) - a, -day)
884 self.assertEqual(a - (a + week), -week)
885 self.assertEqual(a - (a + day), -day)
886 self.assertEqual(a - (a - week), week)
887 self.assertEqual(a - (a - day), day)
888
Mark Dickinson5c2db372009-12-05 20:28:34 +0000889 # Add/sub ints or floats should be illegal
890 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000891 self.assertRaises(TypeError, lambda: a+i)
892 self.assertRaises(TypeError, lambda: a-i)
893 self.assertRaises(TypeError, lambda: i+a)
894 self.assertRaises(TypeError, lambda: i-a)
895
896 # delta - date is senseless.
897 self.assertRaises(TypeError, lambda: day - a)
898 # mixing date and (delta or date) via * or // is senseless
899 self.assertRaises(TypeError, lambda: day * a)
900 self.assertRaises(TypeError, lambda: a * day)
901 self.assertRaises(TypeError, lambda: day // a)
902 self.assertRaises(TypeError, lambda: a // day)
903 self.assertRaises(TypeError, lambda: a * a)
904 self.assertRaises(TypeError, lambda: a // a)
905 # date + date is senseless
906 self.assertRaises(TypeError, lambda: a + a)
907
908 def test_overflow(self):
909 tiny = self.theclass.resolution
910
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000911 for delta in [tiny, timedelta(1), timedelta(2)]:
912 dt = self.theclass.min + delta
913 dt -= delta # no problem
914 self.assertRaises(OverflowError, dt.__sub__, delta)
915 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000916
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000917 dt = self.theclass.max - delta
918 dt += delta # no problem
919 self.assertRaises(OverflowError, dt.__add__, delta)
920 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000921
922 def test_fromtimestamp(self):
923 import time
924
925 # Try an arbitrary fixed value.
926 year, month, day = 1999, 9, 19
927 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
928 d = self.theclass.fromtimestamp(ts)
929 self.assertEqual(d.year, year)
930 self.assertEqual(d.month, month)
931 self.assertEqual(d.day, day)
932
Tim Peters1b6f7a92004-06-20 02:50:16 +0000933 def test_insane_fromtimestamp(self):
934 # It's possible that some platform maps time_t to double,
935 # and that this test will fail there. This test should
936 # exempt such platforms (provided they return reasonable
937 # results!).
938 for insane in -1e200, 1e200:
939 self.assertRaises(ValueError, self.theclass.fromtimestamp,
940 insane)
941
Tim Peters2a799bf2002-12-16 20:18:38 +0000942 def test_today(self):
943 import time
944
945 # We claim that today() is like fromtimestamp(time.time()), so
946 # prove it.
947 for dummy in range(3):
948 today = self.theclass.today()
949 ts = time.time()
950 todayagain = self.theclass.fromtimestamp(ts)
951 if today == todayagain:
952 break
953 # There are several legit reasons that could fail:
954 # 1. It recently became midnight, between the today() and the
955 # time() calls.
956 # 2. The platform time() has such fine resolution that we'll
957 # never get the same value twice.
958 # 3. The platform time() has poor resolution, and we just
959 # happened to call today() right before a resolution quantum
960 # boundary.
961 # 4. The system clock got fiddled between calls.
962 # In any case, wait a little while and try again.
963 time.sleep(0.1)
964
965 # It worked or it didn't. If it didn't, assume it's reason #2, and
966 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000967 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000968 abs(todayagain - today) < timedelta(seconds=0.5))
969
970 def test_weekday(self):
971 for i in range(7):
972 # March 4, 2002 is a Monday
973 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
974 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
975 # January 2, 1956 is a Monday
976 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
977 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
978
979 def test_isocalendar(self):
980 # Check examples from
981 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
982 for i in range(7):
983 d = self.theclass(2003, 12, 22+i)
984 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
985 d = self.theclass(2003, 12, 29) + timedelta(i)
986 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
987 d = self.theclass(2004, 1, 5+i)
988 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
989 d = self.theclass(2009, 12, 21+i)
990 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
991 d = self.theclass(2009, 12, 28) + timedelta(i)
992 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
993 d = self.theclass(2010, 1, 4+i)
994 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
995
996 def test_iso_long_years(self):
997 # Calculate long ISO years and compare to table from
998 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
999 ISO_LONG_YEARS_TABLE = """
1000 4 32 60 88
1001 9 37 65 93
1002 15 43 71 99
1003 20 48 76
1004 26 54 82
1005
1006 105 133 161 189
1007 111 139 167 195
1008 116 144 172
1009 122 150 178
1010 128 156 184
1011
1012 201 229 257 285
1013 207 235 263 291
1014 212 240 268 296
1015 218 246 274
1016 224 252 280
1017
1018 303 331 359 387
1019 308 336 364 392
1020 314 342 370 398
1021 320 348 376
1022 325 353 381
1023 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001024 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001025 L = []
1026 for i in range(400):
1027 d = self.theclass(2000+i, 12, 31)
1028 d1 = self.theclass(1600+i, 12, 31)
1029 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1030 if d.isocalendar()[1] == 53:
1031 L.append(i)
1032 self.assertEqual(L, iso_long_years)
1033
1034 def test_isoformat(self):
1035 t = self.theclass(2, 3, 2)
1036 self.assertEqual(t.isoformat(), "0002-03-02")
1037
1038 def test_ctime(self):
1039 t = self.theclass(2002, 3, 2)
1040 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1041
1042 def test_strftime(self):
1043 t = self.theclass(2005, 3, 2)
1044 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001045 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001046 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001047
1048 self.assertRaises(TypeError, t.strftime) # needs an arg
1049 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1050 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1051
Georg Brandlf78e02b2008-06-10 17:40:04 +00001052 # test that unicode input is allowed (issue 2782)
1053 self.assertEqual(t.strftime("%m"), "03")
1054
Tim Peters2a799bf2002-12-16 20:18:38 +00001055 # A naive object replaces %z and %Z w/ empty strings.
1056 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1057
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001058 #make sure that invalid format specifiers are handled correctly
1059 #self.assertRaises(ValueError, t.strftime, "%e")
1060 #self.assertRaises(ValueError, t.strftime, "%")
1061 #self.assertRaises(ValueError, t.strftime, "%#")
1062
1063 #oh well, some systems just ignore those invalid ones.
1064 #at least, excercise them to make sure that no crashes
1065 #are generated
1066 for f in ["%e", "%", "%#"]:
1067 try:
1068 t.strftime(f)
1069 except ValueError:
1070 pass
1071
1072 #check that this standard extension works
1073 t.strftime("%f")
1074
Georg Brandlf78e02b2008-06-10 17:40:04 +00001075
Eric Smith1ba31142007-09-11 18:06:02 +00001076 def test_format(self):
1077 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001078 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001079
1080 # check that a derived class's __str__() gets called
1081 class A(self.theclass):
1082 def __str__(self):
1083 return 'A'
1084 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001085 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001086
1087 # check that a derived class's strftime gets called
1088 class B(self.theclass):
1089 def strftime(self, format_spec):
1090 return 'B'
1091 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001092 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001093
1094 for fmt in ["m:%m d:%d y:%y",
1095 "m:%m d:%d y:%y H:%H M:%M S:%S",
1096 "%z %Z",
1097 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001098 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1099 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1100 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001101
Tim Peters2a799bf2002-12-16 20:18:38 +00001102 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001103 # XXX: Should min and max respect subclassing?
1104 if issubclass(self.theclass, datetime):
1105 expected_class = datetime
1106 else:
1107 expected_class = date
1108 self.assertIsInstance(self.theclass.min, expected_class)
1109 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001110 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001111 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001112
1113 def test_extreme_timedelta(self):
1114 big = self.theclass.max - self.theclass.min
1115 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1116 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1117 # n == 315537897599999999 ~= 2**58.13
1118 justasbig = timedelta(0, 0, n)
1119 self.assertEqual(big, justasbig)
1120 self.assertEqual(self.theclass.min + big, self.theclass.max)
1121 self.assertEqual(self.theclass.max - big, self.theclass.min)
1122
1123 def test_timetuple(self):
1124 for i in range(7):
1125 # January 2, 1956 is a Monday (0)
1126 d = self.theclass(1956, 1, 2+i)
1127 t = d.timetuple()
1128 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1129 # February 1, 1956 is a Wednesday (2)
1130 d = self.theclass(1956, 2, 1+i)
1131 t = d.timetuple()
1132 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1133 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1134 # of the year.
1135 d = self.theclass(1956, 3, 1+i)
1136 t = d.timetuple()
1137 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1138 self.assertEqual(t.tm_year, 1956)
1139 self.assertEqual(t.tm_mon, 3)
1140 self.assertEqual(t.tm_mday, 1+i)
1141 self.assertEqual(t.tm_hour, 0)
1142 self.assertEqual(t.tm_min, 0)
1143 self.assertEqual(t.tm_sec, 0)
1144 self.assertEqual(t.tm_wday, (3+i)%7)
1145 self.assertEqual(t.tm_yday, 61+i)
1146 self.assertEqual(t.tm_isdst, -1)
1147
1148 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001149 args = 6, 7, 23
1150 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001151 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001152 green = pickler.dumps(orig, proto)
1153 derived = unpickler.loads(green)
1154 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001155
1156 def test_compare(self):
1157 t1 = self.theclass(2, 3, 4)
1158 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001159 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001160 self.assertTrue(t1 <= t2)
1161 self.assertTrue(t1 >= t2)
1162 self.assertTrue(not t1 != t2)
1163 self.assertTrue(not t1 < t2)
1164 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001165
1166 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1167 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001168 self.assertTrue(t1 < t2)
1169 self.assertTrue(t2 > t1)
1170 self.assertTrue(t1 <= t2)
1171 self.assertTrue(t2 >= t1)
1172 self.assertTrue(t1 != t2)
1173 self.assertTrue(t2 != t1)
1174 self.assertTrue(not t1 == t2)
1175 self.assertTrue(not t2 == t1)
1176 self.assertTrue(not t1 > t2)
1177 self.assertTrue(not t2 < t1)
1178 self.assertTrue(not t1 >= t2)
1179 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001180
Tim Peters68124bb2003-02-08 03:46:31 +00001181 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001182 self.assertEqual(t1 == badarg, False)
1183 self.assertEqual(t1 != badarg, True)
1184 self.assertEqual(badarg == t1, False)
1185 self.assertEqual(badarg != t1, True)
1186
Tim Peters2a799bf2002-12-16 20:18:38 +00001187 self.assertRaises(TypeError, lambda: t1 < badarg)
1188 self.assertRaises(TypeError, lambda: t1 > badarg)
1189 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001190 self.assertRaises(TypeError, lambda: badarg <= t1)
1191 self.assertRaises(TypeError, lambda: badarg < t1)
1192 self.assertRaises(TypeError, lambda: badarg > t1)
1193 self.assertRaises(TypeError, lambda: badarg >= t1)
1194
Tim Peters8d81a012003-01-24 22:36:34 +00001195 def test_mixed_compare(self):
1196 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001197
1198 # Our class can be compared for equality to other classes
1199 self.assertEqual(our == 1, False)
1200 self.assertEqual(1 == our, False)
1201 self.assertEqual(our != 1, True)
1202 self.assertEqual(1 != our, True)
1203
1204 # But the ordering is undefined
1205 self.assertRaises(TypeError, lambda: our < 1)
1206 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001207
Guido van Rossum19960592006-08-24 17:29:38 +00001208 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001209
Guido van Rossum19960592006-08-24 17:29:38 +00001210 class SomeClass:
1211 pass
1212
1213 their = SomeClass()
1214 self.assertEqual(our == their, False)
1215 self.assertEqual(their == our, False)
1216 self.assertEqual(our != their, True)
1217 self.assertEqual(their != our, True)
1218 self.assertRaises(TypeError, lambda: our < their)
1219 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001220
Guido van Rossum19960592006-08-24 17:29:38 +00001221 # However, if the other class explicitly defines ordering
1222 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001223
Guido van Rossum19960592006-08-24 17:29:38 +00001224 class LargerThanAnything:
1225 def __lt__(self, other):
1226 return False
1227 def __le__(self, other):
1228 return isinstance(other, LargerThanAnything)
1229 def __eq__(self, other):
1230 return isinstance(other, LargerThanAnything)
1231 def __ne__(self, other):
1232 return not isinstance(other, LargerThanAnything)
1233 def __gt__(self, other):
1234 return not isinstance(other, LargerThanAnything)
1235 def __ge__(self, other):
1236 return True
1237
1238 their = LargerThanAnything()
1239 self.assertEqual(our == their, False)
1240 self.assertEqual(their == our, False)
1241 self.assertEqual(our != their, True)
1242 self.assertEqual(their != our, True)
1243 self.assertEqual(our < their, True)
1244 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001245
Tim Peters2a799bf2002-12-16 20:18:38 +00001246 def test_bool(self):
1247 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001248 self.assertTrue(self.theclass.min)
1249 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001250
Guido van Rossum04110fb2007-08-24 16:32:05 +00001251 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001252 # For nasty technical reasons, we can't handle years before 1900.
1253 cls = self.theclass
1254 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1255 for y in 1, 49, 51, 99, 100, 1000, 1899:
1256 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001257
1258 def test_replace(self):
1259 cls = self.theclass
1260 args = [1, 2, 3]
1261 base = cls(*args)
1262 self.assertEqual(base, base.replace())
1263
1264 i = 0
1265 for name, newval in (("year", 2),
1266 ("month", 3),
1267 ("day", 4)):
1268 newargs = args[:]
1269 newargs[i] = newval
1270 expected = cls(*newargs)
1271 got = base.replace(**{name: newval})
1272 self.assertEqual(expected, got)
1273 i += 1
1274
1275 # Out of bounds.
1276 base = cls(2000, 2, 29)
1277 self.assertRaises(ValueError, base.replace, year=2001)
1278
Tim Petersa98924a2003-05-17 05:55:19 +00001279 def test_subclass_date(self):
1280
1281 class C(self.theclass):
1282 theAnswer = 42
1283
1284 def __new__(cls, *args, **kws):
1285 temp = kws.copy()
1286 extra = temp.pop('extra')
1287 result = self.theclass.__new__(cls, *args, **temp)
1288 result.extra = extra
1289 return result
1290
1291 def newmeth(self, start):
1292 return start + self.year + self.month
1293
1294 args = 2003, 4, 14
1295
1296 dt1 = self.theclass(*args)
1297 dt2 = C(*args, **{'extra': 7})
1298
1299 self.assertEqual(dt2.__class__, C)
1300 self.assertEqual(dt2.theAnswer, 42)
1301 self.assertEqual(dt2.extra, 7)
1302 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1303 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1304
Tim Peters604c0132004-06-07 23:04:33 +00001305 def test_pickling_subclass_date(self):
1306
1307 args = 6, 7, 23
1308 orig = SubclassDate(*args)
1309 for pickler, unpickler, proto in pickle_choices:
1310 green = pickler.dumps(orig, proto)
1311 derived = unpickler.loads(green)
1312 self.assertEqual(orig, derived)
1313
Tim Peters3f606292004-03-21 23:38:41 +00001314 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001315 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001316 # This is a low-overhead backdoor. A user can (by intent or
1317 # mistake) pass a string directly, which (if it's the right length)
1318 # will get treated like a pickle, and bypass the normal sanity
1319 # checks in the constructor. This can create insane objects.
1320 # The constructor doesn't want to burn the time to validate all
1321 # fields, but does check the month field. This stops, e.g.,
1322 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001323 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001324 if not issubclass(self.theclass, datetime):
1325 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001326 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001327 self.assertRaises(TypeError, self.theclass,
1328 base[:2] + month_byte + base[3:])
1329 for ord_byte in range(1, 13):
1330 # This shouldn't blow up because of the month byte alone. If
1331 # the implementation changes to do more-careful checking, it may
1332 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001333 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001334
Tim Peters2a799bf2002-12-16 20:18:38 +00001335#############################################################################
1336# datetime tests
1337
Tim Peters604c0132004-06-07 23:04:33 +00001338class SubclassDatetime(datetime):
1339 sub_var = 1
1340
Tim Peters2a799bf2002-12-16 20:18:38 +00001341class TestDateTime(TestDate):
1342
1343 theclass = datetime
1344
1345 def test_basic_attributes(self):
1346 dt = self.theclass(2002, 3, 1, 12, 0)
1347 self.assertEqual(dt.year, 2002)
1348 self.assertEqual(dt.month, 3)
1349 self.assertEqual(dt.day, 1)
1350 self.assertEqual(dt.hour, 12)
1351 self.assertEqual(dt.minute, 0)
1352 self.assertEqual(dt.second, 0)
1353 self.assertEqual(dt.microsecond, 0)
1354
1355 def test_basic_attributes_nonzero(self):
1356 # Make sure all attributes are non-zero so bugs in
1357 # bit-shifting access show up.
1358 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1359 self.assertEqual(dt.year, 2002)
1360 self.assertEqual(dt.month, 3)
1361 self.assertEqual(dt.day, 1)
1362 self.assertEqual(dt.hour, 12)
1363 self.assertEqual(dt.minute, 59)
1364 self.assertEqual(dt.second, 59)
1365 self.assertEqual(dt.microsecond, 8000)
1366
1367 def test_roundtrip(self):
1368 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1369 self.theclass.now()):
1370 # Verify dt -> string -> datetime identity.
1371 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001372 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001373 s = s[9:]
1374 dt2 = eval(s)
1375 self.assertEqual(dt, dt2)
1376
1377 # Verify identity via reconstructing from pieces.
1378 dt2 = self.theclass(dt.year, dt.month, dt.day,
1379 dt.hour, dt.minute, dt.second,
1380 dt.microsecond)
1381 self.assertEqual(dt, dt2)
1382
1383 def test_isoformat(self):
1384 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1385 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1386 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1387 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001388 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001389 # str is ISO format with the separator forced to a blank.
1390 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1391
1392 t = self.theclass(2, 3, 2)
1393 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1394 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1395 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1396 # str is ISO format with the separator forced to a blank.
1397 self.assertEqual(str(t), "0002-03-02 00:00:00")
1398
Eric Smith1ba31142007-09-11 18:06:02 +00001399 def test_format(self):
1400 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001401 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001402
1403 # check that a derived class's __str__() gets called
1404 class A(self.theclass):
1405 def __str__(self):
1406 return 'A'
1407 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001408 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001409
1410 # check that a derived class's strftime gets called
1411 class B(self.theclass):
1412 def strftime(self, format_spec):
1413 return 'B'
1414 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001415 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001416
1417 for fmt in ["m:%m d:%d y:%y",
1418 "m:%m d:%d y:%y H:%H M:%M S:%S",
1419 "%z %Z",
1420 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001421 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1422 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1423 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001424
Tim Peters2a799bf2002-12-16 20:18:38 +00001425 def test_more_ctime(self):
1426 # Test fields that TestDate doesn't touch.
1427 import time
1428
1429 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1430 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1431 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1432 # out. The difference is that t.ctime() produces " 2" for the day,
1433 # but platform ctime() produces "02" for the day. According to
1434 # C99, t.ctime() is correct here.
1435 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1436
1437 # So test a case where that difference doesn't matter.
1438 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1439 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1440
1441 def test_tz_independent_comparing(self):
1442 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1443 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1444 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1445 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001446 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001447
1448 # Make sure comparison doesn't forget microseconds, and isn't done
1449 # via comparing a float timestamp (an IEEE double doesn't have enough
1450 # precision to span microsecond resolution across years 1 thru 9999,
1451 # so comparing via timestamp necessarily calls some distinct values
1452 # equal).
1453 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1454 us = timedelta(microseconds=1)
1455 dt2 = dt1 + us
1456 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001457 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001458
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001459 def test_strftime_with_bad_tzname_replace(self):
1460 # verify ok if tzinfo.tzname().replace() returns a non-string
1461 class MyTzInfo(FixedOffset):
1462 def tzname(self, dt):
1463 class MyStr(str):
1464 def replace(self, *args):
1465 return None
1466 return MyStr('name')
1467 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1468 self.assertRaises(TypeError, t.strftime, '%Z')
1469
Tim Peters2a799bf2002-12-16 20:18:38 +00001470 def test_bad_constructor_arguments(self):
1471 # bad years
1472 self.theclass(MINYEAR, 1, 1) # no exception
1473 self.theclass(MAXYEAR, 1, 1) # no exception
1474 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1475 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1476 # bad months
1477 self.theclass(2000, 1, 1) # no exception
1478 self.theclass(2000, 12, 1) # no exception
1479 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1480 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1481 # bad days
1482 self.theclass(2000, 2, 29) # no exception
1483 self.theclass(2004, 2, 29) # no exception
1484 self.theclass(2400, 2, 29) # no exception
1485 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1486 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1487 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1488 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1489 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1490 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1491 # bad hours
1492 self.theclass(2000, 1, 31, 0) # no exception
1493 self.theclass(2000, 1, 31, 23) # no exception
1494 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1495 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1496 # bad minutes
1497 self.theclass(2000, 1, 31, 23, 0) # no exception
1498 self.theclass(2000, 1, 31, 23, 59) # no exception
1499 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1500 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1501 # bad seconds
1502 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1503 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1504 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1505 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1506 # bad microseconds
1507 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1508 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1509 self.assertRaises(ValueError, self.theclass,
1510 2000, 1, 31, 23, 59, 59, -1)
1511 self.assertRaises(ValueError, self.theclass,
1512 2000, 1, 31, 23, 59, 59,
1513 1000000)
1514
1515 def test_hash_equality(self):
1516 d = self.theclass(2000, 12, 31, 23, 30, 17)
1517 e = self.theclass(2000, 12, 31, 23, 30, 17)
1518 self.assertEqual(d, e)
1519 self.assertEqual(hash(d), hash(e))
1520
1521 dic = {d: 1}
1522 dic[e] = 2
1523 self.assertEqual(len(dic), 1)
1524 self.assertEqual(dic[d], 2)
1525 self.assertEqual(dic[e], 2)
1526
1527 d = self.theclass(2001, 1, 1, 0, 5, 17)
1528 e = self.theclass(2001, 1, 1, 0, 5, 17)
1529 self.assertEqual(d, e)
1530 self.assertEqual(hash(d), hash(e))
1531
1532 dic = {d: 1}
1533 dic[e] = 2
1534 self.assertEqual(len(dic), 1)
1535 self.assertEqual(dic[d], 2)
1536 self.assertEqual(dic[e], 2)
1537
1538 def test_computations(self):
1539 a = self.theclass(2002, 1, 31)
1540 b = self.theclass(1956, 1, 31)
1541 diff = a-b
1542 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1543 self.assertEqual(diff.seconds, 0)
1544 self.assertEqual(diff.microseconds, 0)
1545 a = self.theclass(2002, 3, 2, 17, 6)
1546 millisec = timedelta(0, 0, 1000)
1547 hour = timedelta(0, 3600)
1548 day = timedelta(1)
1549 week = timedelta(7)
1550 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1551 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1552 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1553 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1554 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1555 self.assertEqual(a - hour, a + -hour)
1556 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1557 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1558 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1559 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1560 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1561 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1562 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1563 self.assertEqual((a + week) - a, week)
1564 self.assertEqual((a + day) - a, day)
1565 self.assertEqual((a + hour) - a, hour)
1566 self.assertEqual((a + millisec) - a, millisec)
1567 self.assertEqual((a - week) - a, -week)
1568 self.assertEqual((a - day) - a, -day)
1569 self.assertEqual((a - hour) - a, -hour)
1570 self.assertEqual((a - millisec) - a, -millisec)
1571 self.assertEqual(a - (a + week), -week)
1572 self.assertEqual(a - (a + day), -day)
1573 self.assertEqual(a - (a + hour), -hour)
1574 self.assertEqual(a - (a + millisec), -millisec)
1575 self.assertEqual(a - (a - week), week)
1576 self.assertEqual(a - (a - day), day)
1577 self.assertEqual(a - (a - hour), hour)
1578 self.assertEqual(a - (a - millisec), millisec)
1579 self.assertEqual(a + (week + day + hour + millisec),
1580 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1581 self.assertEqual(a + (week + day + hour + millisec),
1582 (((a + week) + day) + hour) + millisec)
1583 self.assertEqual(a - (week + day + hour + millisec),
1584 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1585 self.assertEqual(a - (week + day + hour + millisec),
1586 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001587 # Add/sub ints or floats should be illegal
1588 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001589 self.assertRaises(TypeError, lambda: a+i)
1590 self.assertRaises(TypeError, lambda: a-i)
1591 self.assertRaises(TypeError, lambda: i+a)
1592 self.assertRaises(TypeError, lambda: i-a)
1593
1594 # delta - datetime is senseless.
1595 self.assertRaises(TypeError, lambda: day - a)
1596 # mixing datetime and (delta or datetime) via * or // is senseless
1597 self.assertRaises(TypeError, lambda: day * a)
1598 self.assertRaises(TypeError, lambda: a * day)
1599 self.assertRaises(TypeError, lambda: day // a)
1600 self.assertRaises(TypeError, lambda: a // day)
1601 self.assertRaises(TypeError, lambda: a * a)
1602 self.assertRaises(TypeError, lambda: a // a)
1603 # datetime + datetime is senseless
1604 self.assertRaises(TypeError, lambda: a + a)
1605
1606 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001607 args = 6, 7, 23, 20, 59, 1, 64**2
1608 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001609 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001610 green = pickler.dumps(orig, proto)
1611 derived = unpickler.loads(green)
1612 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001613
Guido van Rossum275666f2003-02-07 21:49:01 +00001614 def test_more_pickling(self):
1615 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1616 s = pickle.dumps(a)
1617 b = pickle.loads(s)
1618 self.assertEqual(b.year, 2003)
1619 self.assertEqual(b.month, 2)
1620 self.assertEqual(b.day, 7)
1621
Tim Peters604c0132004-06-07 23:04:33 +00001622 def test_pickling_subclass_datetime(self):
1623 args = 6, 7, 23, 20, 59, 1, 64**2
1624 orig = SubclassDatetime(*args)
1625 for pickler, unpickler, proto in pickle_choices:
1626 green = pickler.dumps(orig, proto)
1627 derived = unpickler.loads(green)
1628 self.assertEqual(orig, derived)
1629
Tim Peters2a799bf2002-12-16 20:18:38 +00001630 def test_more_compare(self):
1631 # The test_compare() inherited from TestDate covers the error cases.
1632 # We just want to test lexicographic ordering on the members datetime
1633 # has that date lacks.
1634 args = [2000, 11, 29, 20, 58, 16, 999998]
1635 t1 = self.theclass(*args)
1636 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001637 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001638 self.assertTrue(t1 <= t2)
1639 self.assertTrue(t1 >= t2)
1640 self.assertTrue(not t1 != t2)
1641 self.assertTrue(not t1 < t2)
1642 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001643
1644 for i in range(len(args)):
1645 newargs = args[:]
1646 newargs[i] = args[i] + 1
1647 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001648 self.assertTrue(t1 < t2)
1649 self.assertTrue(t2 > t1)
1650 self.assertTrue(t1 <= t2)
1651 self.assertTrue(t2 >= t1)
1652 self.assertTrue(t1 != t2)
1653 self.assertTrue(t2 != t1)
1654 self.assertTrue(not t1 == t2)
1655 self.assertTrue(not t2 == t1)
1656 self.assertTrue(not t1 > t2)
1657 self.assertTrue(not t2 < t1)
1658 self.assertTrue(not t1 >= t2)
1659 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001660
1661
1662 # A helper for timestamp constructor tests.
1663 def verify_field_equality(self, expected, got):
1664 self.assertEqual(expected.tm_year, got.year)
1665 self.assertEqual(expected.tm_mon, got.month)
1666 self.assertEqual(expected.tm_mday, got.day)
1667 self.assertEqual(expected.tm_hour, got.hour)
1668 self.assertEqual(expected.tm_min, got.minute)
1669 self.assertEqual(expected.tm_sec, got.second)
1670
1671 def test_fromtimestamp(self):
1672 import time
1673
1674 ts = time.time()
1675 expected = time.localtime(ts)
1676 got = self.theclass.fromtimestamp(ts)
1677 self.verify_field_equality(expected, got)
1678
1679 def test_utcfromtimestamp(self):
1680 import time
1681
1682 ts = time.time()
1683 expected = time.gmtime(ts)
1684 got = self.theclass.utcfromtimestamp(ts)
1685 self.verify_field_equality(expected, got)
1686
Thomas Wouters477c8d52006-05-27 19:21:47 +00001687 def test_microsecond_rounding(self):
1688 # Test whether fromtimestamp "rounds up" floats that are less
1689 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001690 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1691 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001692
Tim Peters1b6f7a92004-06-20 02:50:16 +00001693 def test_insane_fromtimestamp(self):
1694 # It's possible that some platform maps time_t to double,
1695 # and that this test will fail there. This test should
1696 # exempt such platforms (provided they return reasonable
1697 # results!).
1698 for insane in -1e200, 1e200:
1699 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1700 insane)
1701
1702 def test_insane_utcfromtimestamp(self):
1703 # It's possible that some platform maps time_t to double,
1704 # and that this test will fail there. This test should
1705 # exempt such platforms (provided they return reasonable
1706 # results!).
1707 for insane in -1e200, 1e200:
1708 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1709 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001710 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001711 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001712 # The result is tz-dependent; at least test that this doesn't
1713 # fail (like it did before bug 1646728 was fixed).
1714 self.theclass.fromtimestamp(-1.05)
1715
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001716 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001717 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001718 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001719 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001720
Tim Peters2a799bf2002-12-16 20:18:38 +00001721 def test_utcnow(self):
1722 import time
1723
1724 # Call it a success if utcnow() and utcfromtimestamp() are within
1725 # a second of each other.
1726 tolerance = timedelta(seconds=1)
1727 for dummy in range(3):
1728 from_now = self.theclass.utcnow()
1729 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1730 if abs(from_timestamp - from_now) <= tolerance:
1731 break
1732 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001733 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001734
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001735 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001736 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001737
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001738 string = '2004-12-01 13:02:47.197'
1739 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001740 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001741 got = self.theclass.strptime(string, format)
1742 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001743 self.assertIs(type(expected), self.theclass)
1744 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001745
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001746 strptime = self.theclass.strptime
1747 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1748 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1749 # Only local timezone and UTC are supported
1750 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1751 (-_time.timezone, _time.tzname[0])):
1752 if tzseconds < 0:
1753 sign = '-'
1754 seconds = -tzseconds
1755 else:
1756 sign ='+'
1757 seconds = tzseconds
1758 hours, minutes = divmod(seconds//60, 60)
1759 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1760 dt = strptime(dtstr, "%z %Z")
1761 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1762 self.assertEqual(dt.tzname(), tzname)
1763 # Can produce inconsistent datetime
1764 dtstr, fmt = "+1234 UTC", "%z %Z"
1765 dt = strptime(dtstr, fmt)
1766 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1767 self.assertEqual(dt.tzname(), 'UTC')
1768 # yet will roundtrip
1769 self.assertEqual(dt.strftime(fmt), dtstr)
1770
1771 # Produce naive datetime if no %z is provided
1772 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1773
1774 with self.assertRaises(ValueError): strptime("-2400", "%z")
1775 with self.assertRaises(ValueError): strptime("-000", "%z")
1776
Tim Peters2a799bf2002-12-16 20:18:38 +00001777 def test_more_timetuple(self):
1778 # This tests fields beyond those tested by the TestDate.test_timetuple.
1779 t = self.theclass(2004, 12, 31, 6, 22, 33)
1780 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1781 self.assertEqual(t.timetuple(),
1782 (t.year, t.month, t.day,
1783 t.hour, t.minute, t.second,
1784 t.weekday(),
1785 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1786 -1))
1787 tt = t.timetuple()
1788 self.assertEqual(tt.tm_year, t.year)
1789 self.assertEqual(tt.tm_mon, t.month)
1790 self.assertEqual(tt.tm_mday, t.day)
1791 self.assertEqual(tt.tm_hour, t.hour)
1792 self.assertEqual(tt.tm_min, t.minute)
1793 self.assertEqual(tt.tm_sec, t.second)
1794 self.assertEqual(tt.tm_wday, t.weekday())
1795 self.assertEqual(tt.tm_yday, t.toordinal() -
1796 date(t.year, 1, 1).toordinal() + 1)
1797 self.assertEqual(tt.tm_isdst, -1)
1798
1799 def test_more_strftime(self):
1800 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001801 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1802 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1803 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001804
1805 def test_extract(self):
1806 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1807 self.assertEqual(dt.date(), date(2002, 3, 4))
1808 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1809
1810 def test_combine(self):
1811 d = date(2002, 3, 4)
1812 t = time(18, 45, 3, 1234)
1813 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1814 combine = self.theclass.combine
1815 dt = combine(d, t)
1816 self.assertEqual(dt, expected)
1817
1818 dt = combine(time=t, date=d)
1819 self.assertEqual(dt, expected)
1820
1821 self.assertEqual(d, dt.date())
1822 self.assertEqual(t, dt.time())
1823 self.assertEqual(dt, combine(dt.date(), dt.time()))
1824
1825 self.assertRaises(TypeError, combine) # need an arg
1826 self.assertRaises(TypeError, combine, d) # need two args
1827 self.assertRaises(TypeError, combine, t, d) # args reversed
1828 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1829 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1830
Tim Peters12bf3392002-12-24 05:41:27 +00001831 def test_replace(self):
1832 cls = self.theclass
1833 args = [1, 2, 3, 4, 5, 6, 7]
1834 base = cls(*args)
1835 self.assertEqual(base, base.replace())
1836
1837 i = 0
1838 for name, newval in (("year", 2),
1839 ("month", 3),
1840 ("day", 4),
1841 ("hour", 5),
1842 ("minute", 6),
1843 ("second", 7),
1844 ("microsecond", 8)):
1845 newargs = args[:]
1846 newargs[i] = newval
1847 expected = cls(*newargs)
1848 got = base.replace(**{name: newval})
1849 self.assertEqual(expected, got)
1850 i += 1
1851
1852 # Out of bounds.
1853 base = cls(2000, 2, 29)
1854 self.assertRaises(ValueError, base.replace, year=2001)
1855
Tim Peters80475bb2002-12-25 07:40:55 +00001856 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001857 # Pretty boring! The TZ test is more interesting here. astimezone()
1858 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001859 dt = self.theclass.now()
1860 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001861 self.assertRaises(TypeError, dt.astimezone) # not enough args
1862 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1863 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001864 self.assertRaises(ValueError, dt.astimezone, f) # naive
1865 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001866
Tim Peters52dcce22003-01-23 16:36:11 +00001867 class Bogus(tzinfo):
1868 def utcoffset(self, dt): return None
1869 def dst(self, dt): return timedelta(0)
1870 bog = Bogus()
1871 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1872
1873 class AlsoBogus(tzinfo):
1874 def utcoffset(self, dt): return timedelta(0)
1875 def dst(self, dt): return None
1876 alsobog = AlsoBogus()
1877 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001878
Tim Petersa98924a2003-05-17 05:55:19 +00001879 def test_subclass_datetime(self):
1880
1881 class C(self.theclass):
1882 theAnswer = 42
1883
1884 def __new__(cls, *args, **kws):
1885 temp = kws.copy()
1886 extra = temp.pop('extra')
1887 result = self.theclass.__new__(cls, *args, **temp)
1888 result.extra = extra
1889 return result
1890
1891 def newmeth(self, start):
1892 return start + self.year + self.month + self.second
1893
1894 args = 2003, 4, 14, 12, 13, 41
1895
1896 dt1 = self.theclass(*args)
1897 dt2 = C(*args, **{'extra': 7})
1898
1899 self.assertEqual(dt2.__class__, C)
1900 self.assertEqual(dt2.theAnswer, 42)
1901 self.assertEqual(dt2.extra, 7)
1902 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1903 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1904 dt1.second - 7)
1905
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001906class TestSubclassDateTime(TestDateTime):
1907 theclass = SubclassDatetime
1908 # Override tests not designed for subclass
1909 def test_roundtrip(self):
1910 pass
1911
Tim Peters604c0132004-06-07 23:04:33 +00001912class SubclassTime(time):
1913 sub_var = 1
1914
Guido van Rossumd8faa362007-04-27 19:54:29 +00001915class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001916
1917 theclass = time
1918
1919 def test_basic_attributes(self):
1920 t = self.theclass(12, 0)
1921 self.assertEqual(t.hour, 12)
1922 self.assertEqual(t.minute, 0)
1923 self.assertEqual(t.second, 0)
1924 self.assertEqual(t.microsecond, 0)
1925
1926 def test_basic_attributes_nonzero(self):
1927 # Make sure all attributes are non-zero so bugs in
1928 # bit-shifting access show up.
1929 t = self.theclass(12, 59, 59, 8000)
1930 self.assertEqual(t.hour, 12)
1931 self.assertEqual(t.minute, 59)
1932 self.assertEqual(t.second, 59)
1933 self.assertEqual(t.microsecond, 8000)
1934
1935 def test_roundtrip(self):
1936 t = self.theclass(1, 2, 3, 4)
1937
1938 # Verify t -> string -> time identity.
1939 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001940 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001941 s = s[9:]
1942 t2 = eval(s)
1943 self.assertEqual(t, t2)
1944
1945 # Verify identity via reconstructing from pieces.
1946 t2 = self.theclass(t.hour, t.minute, t.second,
1947 t.microsecond)
1948 self.assertEqual(t, t2)
1949
1950 def test_comparing(self):
1951 args = [1, 2, 3, 4]
1952 t1 = self.theclass(*args)
1953 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001954 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001955 self.assertTrue(t1 <= t2)
1956 self.assertTrue(t1 >= t2)
1957 self.assertTrue(not t1 != t2)
1958 self.assertTrue(not t1 < t2)
1959 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001960
1961 for i in range(len(args)):
1962 newargs = args[:]
1963 newargs[i] = args[i] + 1
1964 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001965 self.assertTrue(t1 < t2)
1966 self.assertTrue(t2 > t1)
1967 self.assertTrue(t1 <= t2)
1968 self.assertTrue(t2 >= t1)
1969 self.assertTrue(t1 != t2)
1970 self.assertTrue(t2 != t1)
1971 self.assertTrue(not t1 == t2)
1972 self.assertTrue(not t2 == t1)
1973 self.assertTrue(not t1 > t2)
1974 self.assertTrue(not t2 < t1)
1975 self.assertTrue(not t1 >= t2)
1976 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001977
Tim Peters68124bb2003-02-08 03:46:31 +00001978 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001979 self.assertEqual(t1 == badarg, False)
1980 self.assertEqual(t1 != badarg, True)
1981 self.assertEqual(badarg == t1, False)
1982 self.assertEqual(badarg != t1, True)
1983
Tim Peters2a799bf2002-12-16 20:18:38 +00001984 self.assertRaises(TypeError, lambda: t1 <= badarg)
1985 self.assertRaises(TypeError, lambda: t1 < badarg)
1986 self.assertRaises(TypeError, lambda: t1 > badarg)
1987 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001988 self.assertRaises(TypeError, lambda: badarg <= t1)
1989 self.assertRaises(TypeError, lambda: badarg < t1)
1990 self.assertRaises(TypeError, lambda: badarg > t1)
1991 self.assertRaises(TypeError, lambda: badarg >= t1)
1992
1993 def test_bad_constructor_arguments(self):
1994 # bad hours
1995 self.theclass(0, 0) # no exception
1996 self.theclass(23, 0) # no exception
1997 self.assertRaises(ValueError, self.theclass, -1, 0)
1998 self.assertRaises(ValueError, self.theclass, 24, 0)
1999 # bad minutes
2000 self.theclass(23, 0) # no exception
2001 self.theclass(23, 59) # no exception
2002 self.assertRaises(ValueError, self.theclass, 23, -1)
2003 self.assertRaises(ValueError, self.theclass, 23, 60)
2004 # bad seconds
2005 self.theclass(23, 59, 0) # no exception
2006 self.theclass(23, 59, 59) # no exception
2007 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2008 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2009 # bad microseconds
2010 self.theclass(23, 59, 59, 0) # no exception
2011 self.theclass(23, 59, 59, 999999) # no exception
2012 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2013 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2014
2015 def test_hash_equality(self):
2016 d = self.theclass(23, 30, 17)
2017 e = self.theclass(23, 30, 17)
2018 self.assertEqual(d, e)
2019 self.assertEqual(hash(d), hash(e))
2020
2021 dic = {d: 1}
2022 dic[e] = 2
2023 self.assertEqual(len(dic), 1)
2024 self.assertEqual(dic[d], 2)
2025 self.assertEqual(dic[e], 2)
2026
2027 d = self.theclass(0, 5, 17)
2028 e = self.theclass(0, 5, 17)
2029 self.assertEqual(d, e)
2030 self.assertEqual(hash(d), hash(e))
2031
2032 dic = {d: 1}
2033 dic[e] = 2
2034 self.assertEqual(len(dic), 1)
2035 self.assertEqual(dic[d], 2)
2036 self.assertEqual(dic[e], 2)
2037
2038 def test_isoformat(self):
2039 t = self.theclass(4, 5, 1, 123)
2040 self.assertEqual(t.isoformat(), "04:05:01.000123")
2041 self.assertEqual(t.isoformat(), str(t))
2042
2043 t = self.theclass()
2044 self.assertEqual(t.isoformat(), "00:00:00")
2045 self.assertEqual(t.isoformat(), str(t))
2046
2047 t = self.theclass(microsecond=1)
2048 self.assertEqual(t.isoformat(), "00:00:00.000001")
2049 self.assertEqual(t.isoformat(), str(t))
2050
2051 t = self.theclass(microsecond=10)
2052 self.assertEqual(t.isoformat(), "00:00:00.000010")
2053 self.assertEqual(t.isoformat(), str(t))
2054
2055 t = self.theclass(microsecond=100)
2056 self.assertEqual(t.isoformat(), "00:00:00.000100")
2057 self.assertEqual(t.isoformat(), str(t))
2058
2059 t = self.theclass(microsecond=1000)
2060 self.assertEqual(t.isoformat(), "00:00:00.001000")
2061 self.assertEqual(t.isoformat(), str(t))
2062
2063 t = self.theclass(microsecond=10000)
2064 self.assertEqual(t.isoformat(), "00:00:00.010000")
2065 self.assertEqual(t.isoformat(), str(t))
2066
2067 t = self.theclass(microsecond=100000)
2068 self.assertEqual(t.isoformat(), "00:00:00.100000")
2069 self.assertEqual(t.isoformat(), str(t))
2070
Thomas Wouterscf297e42007-02-23 15:07:44 +00002071 def test_1653736(self):
2072 # verify it doesn't accept extra keyword arguments
2073 t = self.theclass(second=1)
2074 self.assertRaises(TypeError, t.isoformat, foo=3)
2075
Tim Peters2a799bf2002-12-16 20:18:38 +00002076 def test_strftime(self):
2077 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002078 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002079 # A naive object replaces %z and %Z with empty strings.
2080 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2081
Eric Smith1ba31142007-09-11 18:06:02 +00002082 def test_format(self):
2083 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002084 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002085
2086 # check that a derived class's __str__() gets called
2087 class A(self.theclass):
2088 def __str__(self):
2089 return 'A'
2090 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002091 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002092
2093 # check that a derived class's strftime gets called
2094 class B(self.theclass):
2095 def strftime(self, format_spec):
2096 return 'B'
2097 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002098 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002099
2100 for fmt in ['%H %M %S',
2101 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002102 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2103 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2104 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002105
Tim Peters2a799bf2002-12-16 20:18:38 +00002106 def test_str(self):
2107 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2108 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2109 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2110 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2111 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2112
2113 def test_repr(self):
2114 name = 'datetime.' + self.theclass.__name__
2115 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2116 "%s(1, 2, 3, 4)" % name)
2117 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2118 "%s(10, 2, 3, 4000)" % name)
2119 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2120 "%s(0, 2, 3, 400000)" % name)
2121 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2122 "%s(12, 2, 3)" % name)
2123 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2124 "%s(23, 15)" % name)
2125
2126 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002127 self.assertIsInstance(self.theclass.min, self.theclass)
2128 self.assertIsInstance(self.theclass.max, self.theclass)
2129 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002130 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002131
2132 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002133 args = 20, 59, 16, 64**2
2134 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002135 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002136 green = pickler.dumps(orig, proto)
2137 derived = unpickler.loads(green)
2138 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002139
Tim Peters604c0132004-06-07 23:04:33 +00002140 def test_pickling_subclass_time(self):
2141 args = 20, 59, 16, 64**2
2142 orig = SubclassTime(*args)
2143 for pickler, unpickler, proto in pickle_choices:
2144 green = pickler.dumps(orig, proto)
2145 derived = unpickler.loads(green)
2146 self.assertEqual(orig, derived)
2147
Tim Peters2a799bf2002-12-16 20:18:38 +00002148 def test_bool(self):
2149 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002150 self.assertTrue(cls(1))
2151 self.assertTrue(cls(0, 1))
2152 self.assertTrue(cls(0, 0, 1))
2153 self.assertTrue(cls(0, 0, 0, 1))
2154 self.assertTrue(not cls(0))
2155 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002156
Tim Peters12bf3392002-12-24 05:41:27 +00002157 def test_replace(self):
2158 cls = self.theclass
2159 args = [1, 2, 3, 4]
2160 base = cls(*args)
2161 self.assertEqual(base, base.replace())
2162
2163 i = 0
2164 for name, newval in (("hour", 5),
2165 ("minute", 6),
2166 ("second", 7),
2167 ("microsecond", 8)):
2168 newargs = args[:]
2169 newargs[i] = newval
2170 expected = cls(*newargs)
2171 got = base.replace(**{name: newval})
2172 self.assertEqual(expected, got)
2173 i += 1
2174
2175 # Out of bounds.
2176 base = cls(1)
2177 self.assertRaises(ValueError, base.replace, hour=24)
2178 self.assertRaises(ValueError, base.replace, minute=-1)
2179 self.assertRaises(ValueError, base.replace, second=100)
2180 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2181
Tim Petersa98924a2003-05-17 05:55:19 +00002182 def test_subclass_time(self):
2183
2184 class C(self.theclass):
2185 theAnswer = 42
2186
2187 def __new__(cls, *args, **kws):
2188 temp = kws.copy()
2189 extra = temp.pop('extra')
2190 result = self.theclass.__new__(cls, *args, **temp)
2191 result.extra = extra
2192 return result
2193
2194 def newmeth(self, start):
2195 return start + self.hour + self.second
2196
2197 args = 4, 5, 6
2198
2199 dt1 = self.theclass(*args)
2200 dt2 = C(*args, **{'extra': 7})
2201
2202 self.assertEqual(dt2.__class__, C)
2203 self.assertEqual(dt2.theAnswer, 42)
2204 self.assertEqual(dt2.extra, 7)
2205 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2206 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2207
Armin Rigof4afb212005-11-07 07:15:48 +00002208 def test_backdoor_resistance(self):
2209 # see TestDate.test_backdoor_resistance().
2210 base = '2:59.0'
2211 for hour_byte in ' ', '9', chr(24), '\xff':
2212 self.assertRaises(TypeError, self.theclass,
2213 hour_byte + base[1:])
2214
Tim Peters855fe882002-12-22 03:43:39 +00002215# A mixin for classes with a tzinfo= argument. Subclasses must define
2216# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002217# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002218class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002219
Tim Petersbad8ff02002-12-30 20:52:32 +00002220 def test_argument_passing(self):
2221 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002222 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002223 class introspective(tzinfo):
2224 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002225 def utcoffset(self, dt):
2226 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002227 dst = utcoffset
2228
2229 obj = cls(1, 2, 3, tzinfo=introspective())
2230
Tim Peters0bf60bd2003-01-08 20:40:01 +00002231 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002232 self.assertEqual(obj.tzname(), expected)
2233
Tim Peters0bf60bd2003-01-08 20:40:01 +00002234 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002235 self.assertEqual(obj.utcoffset(), expected)
2236 self.assertEqual(obj.dst(), expected)
2237
Tim Peters855fe882002-12-22 03:43:39 +00002238 def test_bad_tzinfo_classes(self):
2239 cls = self.theclass
2240 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002241
Tim Peters855fe882002-12-22 03:43:39 +00002242 class NiceTry(object):
2243 def __init__(self): pass
2244 def utcoffset(self, dt): pass
2245 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2246
2247 class BetterTry(tzinfo):
2248 def __init__(self): pass
2249 def utcoffset(self, dt): pass
2250 b = BetterTry()
2251 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002252 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002253
2254 def test_utc_offset_out_of_bounds(self):
2255 class Edgy(tzinfo):
2256 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002257 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002258 def utcoffset(self, dt):
2259 return self.offset
2260
2261 cls = self.theclass
2262 for offset, legit in ((-1440, False),
2263 (-1439, True),
2264 (1439, True),
2265 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002266 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002267 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002268 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002269 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002270 else:
2271 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002272 if legit:
2273 aofs = abs(offset)
2274 h, m = divmod(aofs, 60)
2275 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002276 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002277 t = t.timetz()
2278 self.assertEqual(str(t), "01:02:03" + tag)
2279 else:
2280 self.assertRaises(ValueError, str, t)
2281
2282 def test_tzinfo_classes(self):
2283 cls = self.theclass
2284 class C1(tzinfo):
2285 def utcoffset(self, dt): return None
2286 def dst(self, dt): return None
2287 def tzname(self, dt): return None
2288 for t in (cls(1, 1, 1),
2289 cls(1, 1, 1, tzinfo=None),
2290 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002291 self.assertTrue(t.utcoffset() is None)
2292 self.assertTrue(t.dst() is None)
2293 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002294
Tim Peters855fe882002-12-22 03:43:39 +00002295 class C3(tzinfo):
2296 def utcoffset(self, dt): return timedelta(minutes=-1439)
2297 def dst(self, dt): return timedelta(minutes=1439)
2298 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002299 t = cls(1, 1, 1, tzinfo=C3())
2300 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2301 self.assertEqual(t.dst(), timedelta(minutes=1439))
2302 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002303
2304 # Wrong types.
2305 class C4(tzinfo):
2306 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002307 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002308 def tzname(self, dt): return 0
2309 t = cls(1, 1, 1, tzinfo=C4())
2310 self.assertRaises(TypeError, t.utcoffset)
2311 self.assertRaises(TypeError, t.dst)
2312 self.assertRaises(TypeError, t.tzname)
2313
2314 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002315 class C6(tzinfo):
2316 def utcoffset(self, dt): return timedelta(hours=-24)
2317 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002318 t = cls(1, 1, 1, tzinfo=C6())
2319 self.assertRaises(ValueError, t.utcoffset)
2320 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002321
2322 # Not a whole number of minutes.
2323 class C7(tzinfo):
2324 def utcoffset(self, dt): return timedelta(seconds=61)
2325 def dst(self, dt): return timedelta(microseconds=-81)
2326 t = cls(1, 1, 1, tzinfo=C7())
2327 self.assertRaises(ValueError, t.utcoffset)
2328 self.assertRaises(ValueError, t.dst)
2329
Tim Peters4c0db782002-12-26 05:01:19 +00002330 def test_aware_compare(self):
2331 cls = self.theclass
2332
Tim Peters60c76e42002-12-27 00:41:11 +00002333 # Ensure that utcoffset() gets ignored if the comparands have
2334 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002335 class OperandDependentOffset(tzinfo):
2336 def utcoffset(self, t):
2337 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002338 # d0 and d1 equal after adjustment
2339 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002340 else:
Tim Peters397301e2003-01-02 21:28:08 +00002341 # d2 off in the weeds
2342 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002343
2344 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2345 d0 = base.replace(minute=3)
2346 d1 = base.replace(minute=9)
2347 d2 = base.replace(minute=11)
2348 for x in d0, d1, d2:
2349 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002350 for op in lt, le, gt, ge, eq, ne:
2351 got = op(x, y)
2352 expected = op(x.minute, y.minute)
2353 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002354
2355 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002356 # Note that a time can't actually have an operand-depedent offset,
2357 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2358 # so skip this test for time.
2359 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002360 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2361 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2362 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2363 for x in d0, d1, d2:
2364 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002365 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002366 if (x is d0 or x is d1) and (y is d0 or y is d1):
2367 expected = 0
2368 elif x is y is d2:
2369 expected = 0
2370 elif x is d2:
2371 expected = -1
2372 else:
2373 assert y is d2
2374 expected = 1
2375 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002376
Tim Peters855fe882002-12-22 03:43:39 +00002377
Tim Peters0bf60bd2003-01-08 20:40:01 +00002378# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002379class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002380 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002381
2382 def test_empty(self):
2383 t = self.theclass()
2384 self.assertEqual(t.hour, 0)
2385 self.assertEqual(t.minute, 0)
2386 self.assertEqual(t.second, 0)
2387 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002388 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002389
Tim Peters2a799bf2002-12-16 20:18:38 +00002390 def test_zones(self):
2391 est = FixedOffset(-300, "EST", 1)
2392 utc = FixedOffset(0, "UTC", -2)
2393 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002394 t1 = time( 7, 47, tzinfo=est)
2395 t2 = time(12, 47, tzinfo=utc)
2396 t3 = time(13, 47, tzinfo=met)
2397 t4 = time(microsecond=40)
2398 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002399
2400 self.assertEqual(t1.tzinfo, est)
2401 self.assertEqual(t2.tzinfo, utc)
2402 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002403 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002404 self.assertEqual(t5.tzinfo, utc)
2405
Tim Peters855fe882002-12-22 03:43:39 +00002406 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2407 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2408 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002409 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002410 self.assertRaises(TypeError, t1.utcoffset, "no args")
2411
2412 self.assertEqual(t1.tzname(), "EST")
2413 self.assertEqual(t2.tzname(), "UTC")
2414 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002415 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002416 self.assertRaises(TypeError, t1.tzname, "no args")
2417
Tim Peters855fe882002-12-22 03:43:39 +00002418 self.assertEqual(t1.dst(), timedelta(minutes=1))
2419 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2420 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002421 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002422 self.assertRaises(TypeError, t1.dst, "no args")
2423
2424 self.assertEqual(hash(t1), hash(t2))
2425 self.assertEqual(hash(t1), hash(t3))
2426 self.assertEqual(hash(t2), hash(t3))
2427
2428 self.assertEqual(t1, t2)
2429 self.assertEqual(t1, t3)
2430 self.assertEqual(t2, t3)
2431 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2432 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2433 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2434
2435 self.assertEqual(str(t1), "07:47:00-05:00")
2436 self.assertEqual(str(t2), "12:47:00+00:00")
2437 self.assertEqual(str(t3), "13:47:00+01:00")
2438 self.assertEqual(str(t4), "00:00:00.000040")
2439 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2440
2441 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2442 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2443 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2444 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2445 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2446
Tim Peters0bf60bd2003-01-08 20:40:01 +00002447 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002448 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2449 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2450 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2451 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2452 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2453
2454 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2455 "07:47:00 %Z=EST %z=-0500")
2456 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2457 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2458
2459 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002460 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002461 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2462 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2463
Tim Petersb92bb712002-12-21 17:44:07 +00002464 # Check that an invalid tzname result raises an exception.
2465 class Badtzname(tzinfo):
2466 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002467 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002468 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2469 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002470
2471 def test_hash_edge_cases(self):
2472 # Offsets that overflow a basic time.
2473 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2474 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2475 self.assertEqual(hash(t1), hash(t2))
2476
2477 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2478 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2479 self.assertEqual(hash(t1), hash(t2))
2480
Tim Peters2a799bf2002-12-16 20:18:38 +00002481 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002482 # Try one without a tzinfo.
2483 args = 20, 59, 16, 64**2
2484 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002485 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002486 green = pickler.dumps(orig, proto)
2487 derived = unpickler.loads(green)
2488 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002489
2490 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002491 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002492 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002493 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002494 green = pickler.dumps(orig, proto)
2495 derived = unpickler.loads(green)
2496 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002497 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002498 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2499 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002500
2501 def test_more_bool(self):
2502 # Test cases with non-None tzinfo.
2503 cls = self.theclass
2504
2505 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002506 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002507
2508 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002509 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002510
2511 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002512 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002513
2514 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002515 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002516
2517 # Mostly ensuring this doesn't overflow internally.
2518 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002519 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002520
2521 # But this should yield a value error -- the utcoffset is bogus.
2522 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2523 self.assertRaises(ValueError, lambda: bool(t))
2524
2525 # Likewise.
2526 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2527 self.assertRaises(ValueError, lambda: bool(t))
2528
Tim Peters12bf3392002-12-24 05:41:27 +00002529 def test_replace(self):
2530 cls = self.theclass
2531 z100 = FixedOffset(100, "+100")
2532 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2533 args = [1, 2, 3, 4, z100]
2534 base = cls(*args)
2535 self.assertEqual(base, base.replace())
2536
2537 i = 0
2538 for name, newval in (("hour", 5),
2539 ("minute", 6),
2540 ("second", 7),
2541 ("microsecond", 8),
2542 ("tzinfo", zm200)):
2543 newargs = args[:]
2544 newargs[i] = newval
2545 expected = cls(*newargs)
2546 got = base.replace(**{name: newval})
2547 self.assertEqual(expected, got)
2548 i += 1
2549
2550 # Ensure we can get rid of a tzinfo.
2551 self.assertEqual(base.tzname(), "+100")
2552 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002553 self.assertTrue(base2.tzinfo is None)
2554 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002555
2556 # Ensure we can add one.
2557 base3 = base2.replace(tzinfo=z100)
2558 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002559 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002560
2561 # Out of bounds.
2562 base = cls(1)
2563 self.assertRaises(ValueError, base.replace, hour=24)
2564 self.assertRaises(ValueError, base.replace, minute=-1)
2565 self.assertRaises(ValueError, base.replace, second=100)
2566 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2567
Tim Peters60c76e42002-12-27 00:41:11 +00002568 def test_mixed_compare(self):
2569 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002570 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002571 self.assertEqual(t1, t2)
2572 t2 = t2.replace(tzinfo=None)
2573 self.assertEqual(t1, t2)
2574 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2575 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002576 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2577 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002578
Tim Peters0bf60bd2003-01-08 20:40:01 +00002579 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002580 class Varies(tzinfo):
2581 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002582 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002583 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002584 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002585 return self.offset
2586
2587 v = Varies()
2588 t1 = t2.replace(tzinfo=v)
2589 t2 = t2.replace(tzinfo=v)
2590 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2591 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2592 self.assertEqual(t1, t2)
2593
2594 # But if they're not identical, it isn't ignored.
2595 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002596 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002597
Tim Petersa98924a2003-05-17 05:55:19 +00002598 def test_subclass_timetz(self):
2599
2600 class C(self.theclass):
2601 theAnswer = 42
2602
2603 def __new__(cls, *args, **kws):
2604 temp = kws.copy()
2605 extra = temp.pop('extra')
2606 result = self.theclass.__new__(cls, *args, **temp)
2607 result.extra = extra
2608 return result
2609
2610 def newmeth(self, start):
2611 return start + self.hour + self.second
2612
2613 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2614
2615 dt1 = self.theclass(*args)
2616 dt2 = C(*args, **{'extra': 7})
2617
2618 self.assertEqual(dt2.__class__, C)
2619 self.assertEqual(dt2.theAnswer, 42)
2620 self.assertEqual(dt2.extra, 7)
2621 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2622 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2623
Tim Peters4c0db782002-12-26 05:01:19 +00002624
Tim Peters0bf60bd2003-01-08 20:40:01 +00002625# Testing datetime objects with a non-None tzinfo.
2626
Guido van Rossumd8faa362007-04-27 19:54:29 +00002627class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002628 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002629
2630 def test_trivial(self):
2631 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2632 self.assertEqual(dt.year, 1)
2633 self.assertEqual(dt.month, 2)
2634 self.assertEqual(dt.day, 3)
2635 self.assertEqual(dt.hour, 4)
2636 self.assertEqual(dt.minute, 5)
2637 self.assertEqual(dt.second, 6)
2638 self.assertEqual(dt.microsecond, 7)
2639 self.assertEqual(dt.tzinfo, None)
2640
2641 def test_even_more_compare(self):
2642 # The test_compare() and test_more_compare() inherited from TestDate
2643 # and TestDateTime covered non-tzinfo cases.
2644
2645 # Smallest possible after UTC adjustment.
2646 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2647 # Largest possible after UTC adjustment.
2648 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2649 tzinfo=FixedOffset(-1439, ""))
2650
2651 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002652 self.assertTrue(t1 < t2)
2653 self.assertTrue(t1 != t2)
2654 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002655
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002656 self.assertEqual(t1, t1)
2657 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002658
2659 # Equal afer adjustment.
2660 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2661 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2662 self.assertEqual(t1, t2)
2663
2664 # Change t1 not to subtract a minute, and t1 should be larger.
2665 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002666 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002667
2668 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2669 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002670 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002671
2672 # Back to the original t1, but make seconds resolve it.
2673 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2674 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002675 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002676
2677 # Likewise, but make microseconds resolve it.
2678 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2679 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002680 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002681
2682 # Make t2 naive and it should fail.
2683 t2 = self.theclass.min
2684 self.assertRaises(TypeError, lambda: t1 == t2)
2685 self.assertEqual(t2, t2)
2686
2687 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2688 class Naive(tzinfo):
2689 def utcoffset(self, dt): return None
2690 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2691 self.assertRaises(TypeError, lambda: t1 == t2)
2692 self.assertEqual(t2, t2)
2693
2694 # OTOH, it's OK to compare two of these mixing the two ways of being
2695 # naive.
2696 t1 = self.theclass(5, 6, 7)
2697 self.assertEqual(t1, t2)
2698
2699 # Try a bogus uctoffset.
2700 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002701 def utcoffset(self, dt):
2702 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002703 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2704 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002705 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002706
Tim Peters2a799bf2002-12-16 20:18:38 +00002707 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002708 # Try one without a tzinfo.
2709 args = 6, 7, 23, 20, 59, 1, 64**2
2710 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002711 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002712 green = pickler.dumps(orig, proto)
2713 derived = unpickler.loads(green)
2714 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002715
2716 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002717 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002718 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002719 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002720 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002721 green = pickler.dumps(orig, proto)
2722 derived = unpickler.loads(green)
2723 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002724 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002725 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2726 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002727
2728 def test_extreme_hashes(self):
2729 # If an attempt is made to hash these via subtracting the offset
2730 # then hashing a datetime object, OverflowError results. The
2731 # Python implementation used to blow up here.
2732 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2733 hash(t)
2734 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2735 tzinfo=FixedOffset(-1439, ""))
2736 hash(t)
2737
2738 # OTOH, an OOB offset should blow up.
2739 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2740 self.assertRaises(ValueError, hash, t)
2741
2742 def test_zones(self):
2743 est = FixedOffset(-300, "EST")
2744 utc = FixedOffset(0, "UTC")
2745 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002746 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2747 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2748 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002749 self.assertEqual(t1.tzinfo, est)
2750 self.assertEqual(t2.tzinfo, utc)
2751 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002752 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2753 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2754 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002755 self.assertEqual(t1.tzname(), "EST")
2756 self.assertEqual(t2.tzname(), "UTC")
2757 self.assertEqual(t3.tzname(), "MET")
2758 self.assertEqual(hash(t1), hash(t2))
2759 self.assertEqual(hash(t1), hash(t3))
2760 self.assertEqual(hash(t2), hash(t3))
2761 self.assertEqual(t1, t2)
2762 self.assertEqual(t1, t3)
2763 self.assertEqual(t2, t3)
2764 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2765 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2766 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002767 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002768 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2769 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2770 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2771
2772 def test_combine(self):
2773 met = FixedOffset(60, "MET")
2774 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002775 tz = time(18, 45, 3, 1234, tzinfo=met)
2776 dt = datetime.combine(d, tz)
2777 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002778 tzinfo=met))
2779
2780 def test_extract(self):
2781 met = FixedOffset(60, "MET")
2782 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2783 self.assertEqual(dt.date(), date(2002, 3, 4))
2784 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002785 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002786
2787 def test_tz_aware_arithmetic(self):
2788 import random
2789
2790 now = self.theclass.now()
2791 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002792 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002793 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002794 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002795 self.assertEqual(nowaware.timetz(), timeaware)
2796
2797 # Can't mix aware and non-aware.
2798 self.assertRaises(TypeError, lambda: now - nowaware)
2799 self.assertRaises(TypeError, lambda: nowaware - now)
2800
Tim Peters0bf60bd2003-01-08 20:40:01 +00002801 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002802 self.assertRaises(TypeError, lambda: now + nowaware)
2803 self.assertRaises(TypeError, lambda: nowaware + now)
2804 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2805
2806 # Subtracting should yield 0.
2807 self.assertEqual(now - now, timedelta(0))
2808 self.assertEqual(nowaware - nowaware, timedelta(0))
2809
2810 # Adding a delta should preserve tzinfo.
2811 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2812 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002813 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002814 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002815 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002816 self.assertEqual(nowawareplus, nowawareplus2)
2817
2818 # that - delta should be what we started with, and that - what we
2819 # started with should be delta.
2820 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002821 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002822 self.assertEqual(nowaware, diff)
2823 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2824 self.assertEqual(nowawareplus - nowaware, delta)
2825
2826 # Make up a random timezone.
2827 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002828 # Attach it to nowawareplus.
2829 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002830 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002831 # Make sure the difference takes the timezone adjustments into account.
2832 got = nowaware - nowawareplus
2833 # Expected: (nowaware base - nowaware offset) -
2834 # (nowawareplus base - nowawareplus offset) =
2835 # (nowaware base - nowawareplus base) +
2836 # (nowawareplus offset - nowaware offset) =
2837 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002838 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002839 self.assertEqual(got, expected)
2840
2841 # Try max possible difference.
2842 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2843 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2844 tzinfo=FixedOffset(-1439, "max"))
2845 maxdiff = max - min
2846 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2847 timedelta(minutes=2*1439))
2848
2849 def test_tzinfo_now(self):
2850 meth = self.theclass.now
2851 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2852 base = meth()
2853 # Try with and without naming the keyword.
2854 off42 = FixedOffset(42, "42")
2855 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002856 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002857 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002858 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002859 # Bad argument with and w/o naming the keyword.
2860 self.assertRaises(TypeError, meth, 16)
2861 self.assertRaises(TypeError, meth, tzinfo=16)
2862 # Bad keyword name.
2863 self.assertRaises(TypeError, meth, tinfo=off42)
2864 # Too many args.
2865 self.assertRaises(TypeError, meth, off42, off42)
2866
Tim Peters10cadce2003-01-23 19:58:02 +00002867 # We don't know which time zone we're in, and don't have a tzinfo
2868 # class to represent it, so seeing whether a tz argument actually
2869 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002870 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002871 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2872 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2873 for dummy in range(3):
2874 now = datetime.now(weirdtz)
2875 self.assertTrue(now.tzinfo is weirdtz)
2876 utcnow = datetime.utcnow().replace(tzinfo=utc)
2877 now2 = utcnow.astimezone(weirdtz)
2878 if abs(now - now2) < timedelta(seconds=30):
2879 break
2880 # Else the code is broken, or more than 30 seconds passed between
2881 # calls; assuming the latter, just try again.
2882 else:
2883 # Three strikes and we're out.
2884 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002885
Tim Peters2a799bf2002-12-16 20:18:38 +00002886 def test_tzinfo_fromtimestamp(self):
2887 import time
2888 meth = self.theclass.fromtimestamp
2889 ts = time.time()
2890 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2891 base = meth(ts)
2892 # Try with and without naming the keyword.
2893 off42 = FixedOffset(42, "42")
2894 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002895 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002896 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002897 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002898 # Bad argument with and w/o naming the keyword.
2899 self.assertRaises(TypeError, meth, ts, 16)
2900 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2901 # Bad keyword name.
2902 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2903 # Too many args.
2904 self.assertRaises(TypeError, meth, ts, off42, off42)
2905 # Too few args.
2906 self.assertRaises(TypeError, meth)
2907
Tim Peters2a44a8d2003-01-23 20:53:10 +00002908 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002909 timestamp = 1000000000
2910 utcdatetime = datetime.utcfromtimestamp(timestamp)
2911 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2912 # But on some flavor of Mac, it's nowhere near that. So we can't have
2913 # any idea here what time that actually is, we can only test that
2914 # relative changes match.
2915 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2916 tz = FixedOffset(utcoffset, "tz", 0)
2917 expected = utcdatetime + utcoffset
2918 got = datetime.fromtimestamp(timestamp, tz)
2919 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002920
Tim Peters2a799bf2002-12-16 20:18:38 +00002921 def test_tzinfo_utcnow(self):
2922 meth = self.theclass.utcnow
2923 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2924 base = meth()
2925 # Try with and without naming the keyword; for whatever reason,
2926 # utcnow() doesn't accept a tzinfo argument.
2927 off42 = FixedOffset(42, "42")
2928 self.assertRaises(TypeError, meth, off42)
2929 self.assertRaises(TypeError, meth, tzinfo=off42)
2930
2931 def test_tzinfo_utcfromtimestamp(self):
2932 import time
2933 meth = self.theclass.utcfromtimestamp
2934 ts = time.time()
2935 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2936 base = meth(ts)
2937 # Try with and without naming the keyword; for whatever reason,
2938 # utcfromtimestamp() doesn't accept a tzinfo argument.
2939 off42 = FixedOffset(42, "42")
2940 self.assertRaises(TypeError, meth, ts, off42)
2941 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2942
2943 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002944 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002945 # DST flag.
2946 class DST(tzinfo):
2947 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002948 if isinstance(dstvalue, int):
2949 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002950 self.dstvalue = dstvalue
2951 def dst(self, dt):
2952 return self.dstvalue
2953
2954 cls = self.theclass
2955 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2956 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2957 t = d.timetuple()
2958 self.assertEqual(1, t.tm_year)
2959 self.assertEqual(1, t.tm_mon)
2960 self.assertEqual(1, t.tm_mday)
2961 self.assertEqual(10, t.tm_hour)
2962 self.assertEqual(20, t.tm_min)
2963 self.assertEqual(30, t.tm_sec)
2964 self.assertEqual(0, t.tm_wday)
2965 self.assertEqual(1, t.tm_yday)
2966 self.assertEqual(flag, t.tm_isdst)
2967
2968 # dst() returns wrong type.
2969 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2970
2971 # dst() at the edge.
2972 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2973 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2974
2975 # dst() out of range.
2976 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2977 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2978
2979 def test_utctimetuple(self):
2980 class DST(tzinfo):
2981 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002982 if isinstance(dstvalue, int):
2983 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002984 self.dstvalue = dstvalue
2985 def dst(self, dt):
2986 return self.dstvalue
2987
2988 cls = self.theclass
2989 # This can't work: DST didn't implement utcoffset.
2990 self.assertRaises(NotImplementedError,
2991 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
2992
2993 class UOFS(DST):
2994 def __init__(self, uofs, dofs=None):
2995 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00002996 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00002997 def utcoffset(self, dt):
2998 return self.uofs
2999
Tim Peters2a799bf2002-12-16 20:18:38 +00003000 for dstvalue in -33, 33, 0, None:
3001 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3002 t = d.utctimetuple()
3003 self.assertEqual(d.year, t.tm_year)
3004 self.assertEqual(d.month, t.tm_mon)
3005 self.assertEqual(d.day, t.tm_mday)
3006 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3007 self.assertEqual(13, t.tm_min)
3008 self.assertEqual(d.second, t.tm_sec)
3009 self.assertEqual(d.weekday(), t.tm_wday)
3010 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3011 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003012 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3013 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003014 self.assertEqual(0, t.tm_isdst)
3015
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003016 # Check that utctimetuple() is the same as
3017 # astimezone(utc).timetuple()
3018 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3019 for tz in [timezone.min, timezone.utc, timezone.max]:
3020 dtz = d.replace(tzinfo=tz)
3021 self.assertEqual(dtz.utctimetuple()[:-1],
3022 dtz.astimezone(timezone.utc).timetuple()[:-1])
3023 # At the edges, UTC adjustment can produce years out-of-range
3024 # for a datetime object. Ensure that an OverflowError is
3025 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003026 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3027 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003028 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003029
3030 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3031 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003032 self.assertRaises(OverflowError, huge.utctimetuple)
3033 # More overflow cases
3034 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3035 self.assertRaises(OverflowError, tiny.utctimetuple)
3036 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3037 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003038
3039 def test_tzinfo_isoformat(self):
3040 zero = FixedOffset(0, "+00:00")
3041 plus = FixedOffset(220, "+03:40")
3042 minus = FixedOffset(-231, "-03:51")
3043 unknown = FixedOffset(None, "")
3044
3045 cls = self.theclass
3046 datestr = '0001-02-03'
3047 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003048 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003049 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3050 timestr = '04:05:59' + (us and '.987001' or '')
3051 ofsstr = ofs is not None and d.tzname() or ''
3052 tailstr = timestr + ofsstr
3053 iso = d.isoformat()
3054 self.assertEqual(iso, datestr + 'T' + tailstr)
3055 self.assertEqual(iso, d.isoformat('T'))
3056 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003057 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003058 self.assertEqual(str(d), datestr + ' ' + tailstr)
3059
Tim Peters12bf3392002-12-24 05:41:27 +00003060 def test_replace(self):
3061 cls = self.theclass
3062 z100 = FixedOffset(100, "+100")
3063 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3064 args = [1, 2, 3, 4, 5, 6, 7, z100]
3065 base = cls(*args)
3066 self.assertEqual(base, base.replace())
3067
3068 i = 0
3069 for name, newval in (("year", 2),
3070 ("month", 3),
3071 ("day", 4),
3072 ("hour", 5),
3073 ("minute", 6),
3074 ("second", 7),
3075 ("microsecond", 8),
3076 ("tzinfo", zm200)):
3077 newargs = args[:]
3078 newargs[i] = newval
3079 expected = cls(*newargs)
3080 got = base.replace(**{name: newval})
3081 self.assertEqual(expected, got)
3082 i += 1
3083
3084 # Ensure we can get rid of a tzinfo.
3085 self.assertEqual(base.tzname(), "+100")
3086 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003087 self.assertTrue(base2.tzinfo is None)
3088 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003089
3090 # Ensure we can add one.
3091 base3 = base2.replace(tzinfo=z100)
3092 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003093 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003094
3095 # Out of bounds.
3096 base = cls(2000, 2, 29)
3097 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003098
Tim Peters80475bb2002-12-25 07:40:55 +00003099 def test_more_astimezone(self):
3100 # The inherited test_astimezone covered some trivial and error cases.
3101 fnone = FixedOffset(None, "None")
3102 f44m = FixedOffset(44, "44")
3103 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3104
Tim Peters10cadce2003-01-23 19:58:02 +00003105 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003106 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003107 # Replacing with degenerate tzinfo raises an exception.
3108 self.assertRaises(ValueError, dt.astimezone, fnone)
3109 # Ditto with None tz.
3110 self.assertRaises(TypeError, dt.astimezone, None)
3111 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003112 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003113 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003114 self.assertEqual(x.date(), dt.date())
3115 self.assertEqual(x.time(), dt.time())
3116
3117 # Replacing with different tzinfo does adjust.
3118 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003119 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003120 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3121 expected = dt - dt.utcoffset() # in effect, convert to UTC
3122 expected += fm5h.utcoffset(dt) # and from there to local time
3123 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3124 self.assertEqual(got.date(), expected.date())
3125 self.assertEqual(got.time(), expected.time())
3126 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003127 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003128 self.assertEqual(got, expected)
3129
Tim Peters4c0db782002-12-26 05:01:19 +00003130 def test_aware_subtract(self):
3131 cls = self.theclass
3132
Tim Peters60c76e42002-12-27 00:41:11 +00003133 # Ensure that utcoffset() is ignored when the operands have the
3134 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003135 class OperandDependentOffset(tzinfo):
3136 def utcoffset(self, t):
3137 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003138 # d0 and d1 equal after adjustment
3139 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003140 else:
Tim Peters397301e2003-01-02 21:28:08 +00003141 # d2 off in the weeds
3142 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003143
3144 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3145 d0 = base.replace(minute=3)
3146 d1 = base.replace(minute=9)
3147 d2 = base.replace(minute=11)
3148 for x in d0, d1, d2:
3149 for y in d0, d1, d2:
3150 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003151 expected = timedelta(minutes=x.minute - y.minute)
3152 self.assertEqual(got, expected)
3153
3154 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3155 # ignored.
3156 base = cls(8, 9, 10, 11, 12, 13, 14)
3157 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3158 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3159 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3160 for x in d0, d1, d2:
3161 for y in d0, d1, d2:
3162 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003163 if (x is d0 or x is d1) and (y is d0 or y is d1):
3164 expected = timedelta(0)
3165 elif x is y is d2:
3166 expected = timedelta(0)
3167 elif x is d2:
3168 expected = timedelta(minutes=(11-59)-0)
3169 else:
3170 assert y is d2
3171 expected = timedelta(minutes=0-(11-59))
3172 self.assertEqual(got, expected)
3173
Tim Peters60c76e42002-12-27 00:41:11 +00003174 def test_mixed_compare(self):
3175 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003176 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003177 self.assertEqual(t1, t2)
3178 t2 = t2.replace(tzinfo=None)
3179 self.assertEqual(t1, t2)
3180 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3181 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003182 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3183 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003184
Tim Peters0bf60bd2003-01-08 20:40:01 +00003185 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003186 class Varies(tzinfo):
3187 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003188 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003189 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003190 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003191 return self.offset
3192
3193 v = Varies()
3194 t1 = t2.replace(tzinfo=v)
3195 t2 = t2.replace(tzinfo=v)
3196 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3197 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3198 self.assertEqual(t1, t2)
3199
3200 # But if they're not identical, it isn't ignored.
3201 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003202 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003203
Tim Petersa98924a2003-05-17 05:55:19 +00003204 def test_subclass_datetimetz(self):
3205
3206 class C(self.theclass):
3207 theAnswer = 42
3208
3209 def __new__(cls, *args, **kws):
3210 temp = kws.copy()
3211 extra = temp.pop('extra')
3212 result = self.theclass.__new__(cls, *args, **temp)
3213 result.extra = extra
3214 return result
3215
3216 def newmeth(self, start):
3217 return start + self.hour + self.year
3218
3219 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3220
3221 dt1 = self.theclass(*args)
3222 dt2 = C(*args, **{'extra': 7})
3223
3224 self.assertEqual(dt2.__class__, C)
3225 self.assertEqual(dt2.theAnswer, 42)
3226 self.assertEqual(dt2.extra, 7)
3227 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3228 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3229
Tim Peters621818b2002-12-29 23:44:49 +00003230# Pain to set up DST-aware tzinfo classes.
3231
3232def first_sunday_on_or_after(dt):
3233 days_to_go = 6 - dt.weekday()
3234 if days_to_go:
3235 dt += timedelta(days_to_go)
3236 return dt
3237
3238ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003239MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003240HOUR = timedelta(hours=1)
3241DAY = timedelta(days=1)
3242# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3243DSTSTART = datetime(1, 4, 1, 2)
3244# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003245# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3246# being standard time on that day, there is no spelling in local time of
3247# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3248DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003249
3250class USTimeZone(tzinfo):
3251
3252 def __init__(self, hours, reprname, stdname, dstname):
3253 self.stdoffset = timedelta(hours=hours)
3254 self.reprname = reprname
3255 self.stdname = stdname
3256 self.dstname = dstname
3257
3258 def __repr__(self):
3259 return self.reprname
3260
3261 def tzname(self, dt):
3262 if self.dst(dt):
3263 return self.dstname
3264 else:
3265 return self.stdname
3266
3267 def utcoffset(self, dt):
3268 return self.stdoffset + self.dst(dt)
3269
3270 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003271 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003272 # An exception instead may be sensible here, in one or more of
3273 # the cases.
3274 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003275 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003276
3277 # Find first Sunday in April.
3278 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3279 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3280
3281 # Find last Sunday in October.
3282 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3283 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3284
Tim Peters621818b2002-12-29 23:44:49 +00003285 # Can't compare naive to aware objects, so strip the timezone from
3286 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003287 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003288 return HOUR
3289 else:
3290 return ZERO
3291
Tim Peters521fc152002-12-31 17:36:56 +00003292Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3293Central = USTimeZone(-6, "Central", "CST", "CDT")
3294Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3295Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003296utc_real = FixedOffset(0, "UTC", 0)
3297# For better test coverage, we want another flavor of UTC that's west of
3298# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003299utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003300
3301class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003302 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003303 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003304 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003305
Tim Peters0bf60bd2003-01-08 20:40:01 +00003306 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003307
Tim Peters521fc152002-12-31 17:36:56 +00003308 # Check a time that's inside DST.
3309 def checkinside(self, dt, tz, utc, dston, dstoff):
3310 self.assertEqual(dt.dst(), HOUR)
3311
3312 # Conversion to our own timezone is always an identity.
3313 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003314
3315 asutc = dt.astimezone(utc)
3316 there_and_back = asutc.astimezone(tz)
3317
3318 # Conversion to UTC and back isn't always an identity here,
3319 # because there are redundant spellings (in local time) of
3320 # UTC time when DST begins: the clock jumps from 1:59:59
3321 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3322 # make sense then. The classes above treat 2:MM:SS as
3323 # daylight time then (it's "after 2am"), really an alias
3324 # for 1:MM:SS standard time. The latter form is what
3325 # conversion back from UTC produces.
3326 if dt.date() == dston.date() and dt.hour == 2:
3327 # We're in the redundant hour, and coming back from
3328 # UTC gives the 1:MM:SS standard-time spelling.
3329 self.assertEqual(there_and_back + HOUR, dt)
3330 # Although during was considered to be in daylight
3331 # time, there_and_back is not.
3332 self.assertEqual(there_and_back.dst(), ZERO)
3333 # They're the same times in UTC.
3334 self.assertEqual(there_and_back.astimezone(utc),
3335 dt.astimezone(utc))
3336 else:
3337 # We're not in the redundant hour.
3338 self.assertEqual(dt, there_and_back)
3339
Tim Peters327098a2003-01-20 22:54:38 +00003340 # Because we have a redundant spelling when DST begins, there is
3341 # (unforunately) an hour when DST ends that can't be spelled at all in
3342 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3343 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3344 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3345 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3346 # expressed in local time. Nevertheless, we want conversion back
3347 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003348 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003349 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003350 if dt.date() == dstoff.date() and dt.hour == 0:
3351 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003352 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003353 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3354 nexthour_utc += HOUR
3355 nexthour_tz = nexthour_utc.astimezone(tz)
3356 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003357 else:
Tim Peters327098a2003-01-20 22:54:38 +00003358 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003359
3360 # Check a time that's outside DST.
3361 def checkoutside(self, dt, tz, utc):
3362 self.assertEqual(dt.dst(), ZERO)
3363
3364 # Conversion to our own timezone is always an identity.
3365 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003366
3367 # Converting to UTC and back is an identity too.
3368 asutc = dt.astimezone(utc)
3369 there_and_back = asutc.astimezone(tz)
3370 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003371
Tim Peters1024bf82002-12-30 17:09:40 +00003372 def convert_between_tz_and_utc(self, tz, utc):
3373 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003374 # Because 1:MM on the day DST ends is taken as being standard time,
3375 # there is no spelling in tz for the last hour of daylight time.
3376 # For purposes of the test, the last hour of DST is 0:MM, which is
3377 # taken as being daylight time (and 1:MM is taken as being standard
3378 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003379 dstoff = self.dstoff.replace(tzinfo=tz)
3380 for delta in (timedelta(weeks=13),
3381 DAY,
3382 HOUR,
3383 timedelta(minutes=1),
3384 timedelta(microseconds=1)):
3385
Tim Peters521fc152002-12-31 17:36:56 +00003386 self.checkinside(dston, tz, utc, dston, dstoff)
3387 for during in dston + delta, dstoff - delta:
3388 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003389
Tim Peters521fc152002-12-31 17:36:56 +00003390 self.checkoutside(dstoff, tz, utc)
3391 for outside in dston - delta, dstoff + delta:
3392 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003393
Tim Peters621818b2002-12-29 23:44:49 +00003394 def test_easy(self):
3395 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003396 self.convert_between_tz_and_utc(Eastern, utc_real)
3397 self.convert_between_tz_and_utc(Pacific, utc_real)
3398 self.convert_between_tz_and_utc(Eastern, utc_fake)
3399 self.convert_between_tz_and_utc(Pacific, utc_fake)
3400 # The next is really dancing near the edge. It works because
3401 # Pacific and Eastern are far enough apart that their "problem
3402 # hours" don't overlap.
3403 self.convert_between_tz_and_utc(Eastern, Pacific)
3404 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003405 # OTOH, these fail! Don't enable them. The difficulty is that
3406 # the edge case tests assume that every hour is representable in
3407 # the "utc" class. This is always true for a fixed-offset tzinfo
3408 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3409 # For these adjacent DST-aware time zones, the range of time offsets
3410 # tested ends up creating hours in the one that aren't representable
3411 # in the other. For the same reason, we would see failures in the
3412 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3413 # offset deltas in convert_between_tz_and_utc().
3414 #
3415 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3416 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003417
Tim Petersf3615152003-01-01 21:51:37 +00003418 def test_tricky(self):
3419 # 22:00 on day before daylight starts.
3420 fourback = self.dston - timedelta(hours=4)
3421 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003422 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003423 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3424 # 2", we should get the 3 spelling.
3425 # If we plug 22:00 the day before into Eastern, it "looks like std
3426 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3427 # to 22:00 lands on 2:00, which makes no sense in local time (the
3428 # local clock jumps from 1 to 3). The point here is to make sure we
3429 # get the 3 spelling.
3430 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003431 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003432 self.assertEqual(expected, got)
3433
3434 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3435 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003436 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003437 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3438 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3439 # spelling.
3440 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003441 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003442 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003443
Tim Petersadf64202003-01-04 06:03:15 +00003444 # Now on the day DST ends, we want "repeat an hour" behavior.
3445 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3446 # EST 23:MM 0:MM 1:MM 2:MM
3447 # EDT 0:MM 1:MM 2:MM 3:MM
3448 # wall 0:MM 1:MM 1:MM 2:MM against these
3449 for utc in utc_real, utc_fake:
3450 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003451 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003452 # Convert that to UTC.
3453 first_std_hour -= tz.utcoffset(None)
3454 # Adjust for possibly fake UTC.
3455 asutc = first_std_hour + utc.utcoffset(None)
3456 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3457 # tz=Eastern.
3458 asutcbase = asutc.replace(tzinfo=utc)
3459 for tzhour in (0, 1, 1, 2):
3460 expectedbase = self.dstoff.replace(hour=tzhour)
3461 for minute in 0, 30, 59:
3462 expected = expectedbase.replace(minute=minute)
3463 asutc = asutcbase.replace(minute=minute)
3464 astz = asutc.astimezone(tz)
3465 self.assertEqual(astz.replace(tzinfo=None), expected)
3466 asutcbase += HOUR
3467
3468
Tim Peters710fb152003-01-02 19:35:54 +00003469 def test_bogus_dst(self):
3470 class ok(tzinfo):
3471 def utcoffset(self, dt): return HOUR
3472 def dst(self, dt): return HOUR
3473
3474 now = self.theclass.now().replace(tzinfo=utc_real)
3475 # Doesn't blow up.
3476 now.astimezone(ok())
3477
3478 # Does blow up.
3479 class notok(ok):
3480 def dst(self, dt): return None
3481 self.assertRaises(ValueError, now.astimezone, notok())
3482
Tim Peters52dcce22003-01-23 16:36:11 +00003483 def test_fromutc(self):
3484 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3485 now = datetime.utcnow().replace(tzinfo=utc_real)
3486 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3487 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3488 enow = Eastern.fromutc(now) # doesn't blow up
3489 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3490 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3491 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3492
3493 # Always converts UTC to standard time.
3494 class FauxUSTimeZone(USTimeZone):
3495 def fromutc(self, dt):
3496 return dt + self.stdoffset
3497 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3498
3499 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3500 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3501 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3502
3503 # Check around DST start.
3504 start = self.dston.replace(hour=4, tzinfo=Eastern)
3505 fstart = start.replace(tzinfo=FEastern)
3506 for wall in 23, 0, 1, 3, 4, 5:
3507 expected = start.replace(hour=wall)
3508 if wall == 23:
3509 expected -= timedelta(days=1)
3510 got = Eastern.fromutc(start)
3511 self.assertEqual(expected, got)
3512
3513 expected = fstart + FEastern.stdoffset
3514 got = FEastern.fromutc(fstart)
3515 self.assertEqual(expected, got)
3516
3517 # Ensure astimezone() calls fromutc() too.
3518 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3519 self.assertEqual(expected, got)
3520
3521 start += HOUR
3522 fstart += HOUR
3523
3524 # Check around DST end.
3525 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3526 fstart = start.replace(tzinfo=FEastern)
3527 for wall in 0, 1, 1, 2, 3, 4:
3528 expected = start.replace(hour=wall)
3529 got = Eastern.fromutc(start)
3530 self.assertEqual(expected, got)
3531
3532 expected = fstart + FEastern.stdoffset
3533 got = FEastern.fromutc(fstart)
3534 self.assertEqual(expected, got)
3535
3536 # Ensure astimezone() calls fromutc() too.
3537 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3538 self.assertEqual(expected, got)
3539
3540 start += HOUR
3541 fstart += HOUR
3542
Tim Peters710fb152003-01-02 19:35:54 +00003543
Tim Peters528ca532004-09-16 01:30:50 +00003544#############################################################################
3545# oddballs
3546
3547class Oddballs(unittest.TestCase):
3548
3549 def test_bug_1028306(self):
3550 # Trying to compare a date to a datetime should act like a mixed-
3551 # type comparison, despite that datetime is a subclass of date.
3552 as_date = date.today()
3553 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003554 self.assertTrue(as_date != as_datetime)
3555 self.assertTrue(as_datetime != as_date)
3556 self.assertTrue(not as_date == as_datetime)
3557 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003558 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3559 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3560 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3561 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3562 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3563 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3564 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3565 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3566
3567 # Neverthelss, comparison should work with the base-class (date)
3568 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003569 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003570 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003571 as_different = as_datetime.replace(day= different_day)
3572 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003573
3574 # And date should compare with other subclasses of date. If a
3575 # subclass wants to stop this, it's up to the subclass to do so.
3576 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3577 self.assertEqual(as_date, date_sc)
3578 self.assertEqual(date_sc, as_date)
3579
3580 # Ditto for datetimes.
3581 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3582 as_date.day, 0, 0, 0)
3583 self.assertEqual(as_datetime, datetime_sc)
3584 self.assertEqual(datetime_sc, as_datetime)
3585
Tim Peters2a799bf2002-12-16 20:18:38 +00003586def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003587 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003588
3589if __name__ == "__main__":
3590 test_main()