blob: 7048a11d24fd3d8717c970c1f1ae57b86af514a0 [file] [log] [blame]
Tim Peters0bf60bd2003-01-08 20:40:01 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
Tim Peters2a799bf2002-12-16 20:18:38 +00005
Alexander Belopolskyd5442cd2010-05-26 20:00:12 +00006import sys
Guido van Rossum177e41a2003-01-30 22:06:23 +00007import pickle
Tim Peters2a799bf2002-12-16 20:18:38 +00008import unittest
9
Mark Dickinson7c186e22010-04-20 22:32:49 +000010from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
Mark Dickinsona56c4672009-01-27 18:17:45 +000011
Benjamin Petersonee8712c2008-05-20 21:35:26 +000012from test import support
Tim Peters2a799bf2002-12-16 20:18:38 +000013
14from datetime import MINYEAR, MAXYEAR
15from datetime import timedelta
16from datetime import tzinfo
Tim Peters0bf60bd2003-01-08 20:40:01 +000017from datetime import time
Alexander Belopolsky4e749a12010-06-14 14:15:50 +000018from datetime import timezone
Tim Peters0bf60bd2003-01-08 20:40:01 +000019from datetime import date, datetime
Alexander Belopolskyca94f552010-06-17 18:30:34 +000020import time as _time
Tim Peters0bf60bd2003-01-08 20:40:01 +000021
Alexander Belopolsky1b7046b2010-06-23 21:40:15 +000022pickle_choices = [(pickle, pickle, proto) for proto in range(4)]
23assert len(pickle_choices) == 4
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)
Alexander Belopolsky1b7046b2010-06-23 21:40:15 +0000125 for otype, args in [
126 (PicklableFixedOffset, (offset, 'cookie')),
127 (timezone, (offset,)),
128 (timezone, (offset, "EST"))]:
129 orig = otype(*args)
130 oname = orig.tzname(None)
131 self.assertIsInstance(orig, tzinfo)
132 self.assertIs(type(orig), otype)
133 self.assertEqual(orig.utcoffset(None), offset)
134 self.assertEqual(orig.tzname(None), oname)
135 for pickler, unpickler, proto in pickle_choices:
136 green = pickler.dumps(orig, proto)
137 derived = unpickler.loads(green)
138 self.assertIsInstance(derived, tzinfo)
139 self.assertIs(type(derived), otype)
140 self.assertEqual(derived.utcoffset(None), offset)
141 self.assertEqual(derived.tzname(None), oname)
Tim Peters2a799bf2002-12-16 20:18:38 +0000142
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000143class TestTimeZone(unittest.TestCase):
144
145 def setUp(self):
146 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
147 self.EST = timezone(-timedelta(hours=5), 'EST')
148 self.DT = datetime(2010, 1, 1)
149
150 def test_str(self):
151 for tz in [self.ACDT, self.EST, timezone.utc,
152 timezone.min, timezone.max]:
153 self.assertEqual(str(tz), tz.tzname(None))
154
155 def test_class_members(self):
156 limit = timedelta(hours=23, minutes=59)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000157 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
158 self.assertEqual(timezone.min.utcoffset(None), -limit)
159 self.assertEqual(timezone.max.utcoffset(None), limit)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000160
161
162 def test_constructor(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000163 self.assertEqual(timezone.utc, timezone(timedelta(0)))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000164 # invalid offsets
165 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
166 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
167 self.assertRaises(ValueError, timezone, invalid)
168 self.assertRaises(ValueError, timezone, -invalid)
169
170 with self.assertRaises(TypeError): timezone(None)
171 with self.assertRaises(TypeError): timezone(42)
172 with self.assertRaises(TypeError): timezone(ZERO, None)
173 with self.assertRaises(TypeError): timezone(ZERO, 42)
174
175 def test_inheritance(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000176 self.assertIsInstance(timezone.utc, tzinfo)
177 self.assertIsInstance(self.EST, tzinfo)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000178
179 def test_utcoffset(self):
180 dummy = self.DT
181 for h in [0, 1.5, 12]:
182 offset = h * HOUR
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000183 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
184 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000185
186 with self.assertRaises(TypeError): self.EST.utcoffset('')
187 with self.assertRaises(TypeError): self.EST.utcoffset(5)
188
189
190 def test_dst(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000191 self.assertEqual(None, timezone.utc.dst(self.DT))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000192
193 with self.assertRaises(TypeError): self.EST.dst('')
194 with self.assertRaises(TypeError): self.EST.dst(5)
195
196 def test_tzname(self):
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000197 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
198 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
199 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
200 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
201 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000202
203 with self.assertRaises(TypeError): self.EST.tzname('')
204 with self.assertRaises(TypeError): self.EST.tzname(5)
205
206 def test_fromutc(self):
207 with self.assertRaises(ValueError):
208 timezone.utc.fromutc(self.DT)
209 for tz in [self.EST, self.ACDT, Eastern]:
210 utctime = self.DT.replace(tzinfo=tz)
211 local = tz.fromutc(utctime)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000212 self.assertEqual(local - utctime, tz.utcoffset(local))
213 self.assertEqual(local,
214 self.DT.replace(tzinfo=timezone.utc))
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000215
216 def test_comparison(self):
217 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
218 self.assertEqual(timezone(HOUR), timezone(HOUR))
219 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
220 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
221 self.assertIn(timezone(ZERO), {timezone(ZERO)})
222
223 def test_aware_datetime(self):
224 # test that timezone instances can be used by datetime
225 t = datetime(1, 1, 1)
226 for tz in [timezone.min, timezone.max, timezone.utc]:
Alexander Belopolsky05cc2032010-06-15 18:40:23 +0000227 self.assertEqual(tz.tzname(t),
228 t.replace(tzinfo=tz).tzname())
229 self.assertEqual(tz.utcoffset(t),
230 t.replace(tzinfo=tz).utcoffset())
231 self.assertEqual(tz.dst(t),
232 t.replace(tzinfo=tz).dst())
Alexander Belopolsky4e749a12010-06-14 14:15:50 +0000233
Tim Peters2a799bf2002-12-16 20:18:38 +0000234#############################################################################
Tim Peters07534a62003-02-07 22:50:28 +0000235# Base clase for testing a particular aspect of timedelta, time, date and
236# datetime comparisons.
237
Guido van Rossumd8faa362007-04-27 19:54:29 +0000238class HarmlessMixedComparison:
Tim Peters07534a62003-02-07 22:50:28 +0000239 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
240
241 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
242 # legit constructor.
243
244 def test_harmless_mixed_comparison(self):
245 me = self.theclass(1, 1, 1)
246
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000247 self.assertFalse(me == ())
248 self.assertTrue(me != ())
249 self.assertFalse(() == me)
250 self.assertTrue(() != me)
Tim Peters07534a62003-02-07 22:50:28 +0000251
Benjamin Peterson577473f2010-01-19 00:09:57 +0000252 self.assertIn(me, [1, 20, [], me])
Benjamin Peterson577473f2010-01-19 00:09:57 +0000253 self.assertIn([], [me, 1, 20, []])
Tim Peters07534a62003-02-07 22:50:28 +0000254
255 def test_harmful_mixed_comparison(self):
256 me = self.theclass(1, 1, 1)
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
263 self.assertRaises(TypeError, lambda: () < me)
264 self.assertRaises(TypeError, lambda: () <= me)
265 self.assertRaises(TypeError, lambda: () > me)
266 self.assertRaises(TypeError, lambda: () >= me)
267
Tim Peters07534a62003-02-07 22:50:28 +0000268#############################################################################
Tim Peters2a799bf2002-12-16 20:18:38 +0000269# timedelta tests
270
Guido van Rossumd8faa362007-04-27 19:54:29 +0000271class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
Tim Peters07534a62003-02-07 22:50:28 +0000272
273 theclass = timedelta
Tim Peters2a799bf2002-12-16 20:18:38 +0000274
275 def test_constructor(self):
276 eq = self.assertEqual
277 td = timedelta
278
279 # Check keyword args to constructor
280 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
281 milliseconds=0, microseconds=0))
282 eq(td(1), td(days=1))
283 eq(td(0, 1), td(seconds=1))
284 eq(td(0, 0, 1), td(microseconds=1))
285 eq(td(weeks=1), td(days=7))
286 eq(td(days=1), td(hours=24))
287 eq(td(hours=1), td(minutes=60))
288 eq(td(minutes=1), td(seconds=60))
289 eq(td(seconds=1), td(milliseconds=1000))
290 eq(td(milliseconds=1), td(microseconds=1000))
291
292 # Check float args to constructor
293 eq(td(weeks=1.0/7), td(days=1))
294 eq(td(days=1.0/24), td(hours=1))
295 eq(td(hours=1.0/60), td(minutes=1))
296 eq(td(minutes=1.0/60), td(seconds=1))
297 eq(td(seconds=0.001), td(milliseconds=1))
298 eq(td(milliseconds=0.001), td(microseconds=1))
299
300 def test_computations(self):
301 eq = self.assertEqual
302 td = timedelta
303
304 a = td(7) # One week
305 b = td(0, 60) # One minute
306 c = td(0, 0, 1000) # One millisecond
307 eq(a+b+c, td(7, 60, 1000))
308 eq(a-b, td(6, 24*3600 - 60))
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000309 eq(b.__rsub__(a), td(6, 24*3600 - 60))
Tim Peters2a799bf2002-12-16 20:18:38 +0000310 eq(-a, td(-7))
311 eq(+a, td(7))
312 eq(-b, td(-1, 24*3600 - 60))
313 eq(-c, td(-1, 24*3600 - 1, 999000))
314 eq(abs(a), a)
315 eq(abs(-a), a)
316 eq(td(6, 24*3600), a)
317 eq(td(0, 0, 60*1000000), b)
318 eq(a*10, td(70))
319 eq(a*10, 10*a)
Guido van Rossume2a383d2007-01-15 16:59:06 +0000320 eq(a*10, 10*a)
Tim Peters2a799bf2002-12-16 20:18:38 +0000321 eq(b*10, td(0, 600))
322 eq(10*b, td(0, 600))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000323 eq(b*10, td(0, 600))
Tim Peters2a799bf2002-12-16 20:18:38 +0000324 eq(c*10, td(0, 0, 10000))
325 eq(10*c, td(0, 0, 10000))
Guido van Rossume2a383d2007-01-15 16:59:06 +0000326 eq(c*10, td(0, 0, 10000))
Tim Peters2a799bf2002-12-16 20:18:38 +0000327 eq(a*-1, -a)
328 eq(b*-2, -b-b)
329 eq(c*-2, -c+-c)
330 eq(b*(60*24), (b*60)*24)
331 eq(b*(60*24), (60*b)*24)
332 eq(c*1000, td(0, 1))
333 eq(1000*c, td(0, 1))
334 eq(a//7, td(1))
335 eq(b//10, td(0, 6))
336 eq(c//1000, td(0, 0, 1))
337 eq(a//10, td(0, 7*24*360))
338 eq(a//3600000, td(0, 0, 7*24*1000))
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000339 eq(a/0.5, td(14))
340 eq(b/0.5, td(0, 120))
341 eq(a/7, td(1))
342 eq(b/10, td(0, 6))
343 eq(c/1000, td(0, 0, 1))
344 eq(a/10, td(0, 7*24*360))
345 eq(a/3600000, td(0, 0, 7*24*1000))
346
347 # Multiplication by float
348 us = td(microseconds=1)
349 eq((3*us) * 0.5, 2*us)
350 eq((5*us) * 0.5, 2*us)
351 eq(0.5 * (3*us), 2*us)
352 eq(0.5 * (5*us), 2*us)
353 eq((-3*us) * 0.5, -2*us)
354 eq((-5*us) * 0.5, -2*us)
355
356 # Division by int and float
357 eq((3*us) / 2, 2*us)
358 eq((5*us) / 2, 2*us)
359 eq((-3*us) / 2.0, -2*us)
360 eq((-5*us) / 2.0, -2*us)
361 eq((3*us) / -2, -2*us)
362 eq((5*us) / -2, -2*us)
363 eq((3*us) / -2.0, -2*us)
364 eq((5*us) / -2.0, -2*us)
365 for i in range(-10, 10):
366 eq((i*us/3)//us, round(i/3))
367 for i in range(-10, 10):
368 eq((i*us/-3)//us, round(i/-3))
Tim Peters2a799bf2002-12-16 20:18:38 +0000369
370 def test_disallowed_computations(self):
371 a = timedelta(42)
372
Mark Dickinson5c2db372009-12-05 20:28:34 +0000373 # Add/sub ints or floats should be illegal
374 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000375 self.assertRaises(TypeError, lambda: a+i)
376 self.assertRaises(TypeError, lambda: a-i)
377 self.assertRaises(TypeError, lambda: i+a)
378 self.assertRaises(TypeError, lambda: i-a)
379
Benjamin Petersonf10a79a2008-10-11 00:49:57 +0000380 # Division of int by timedelta doesn't make sense.
Tim Peters2a799bf2002-12-16 20:18:38 +0000381 # Division by zero doesn't make sense.
Mark Dickinson5c2db372009-12-05 20:28:34 +0000382 zero = 0
383 self.assertRaises(TypeError, lambda: zero // a)
384 self.assertRaises(ZeroDivisionError, lambda: a // zero)
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000385 self.assertRaises(ZeroDivisionError, lambda: a / zero)
386 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000387 self.assertRaises(TypeError, lambda: a / '')
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000388
389 @requires_IEEE_754
390 def test_disallowed_special(self):
391 a = timedelta(42)
392 self.assertRaises(ValueError, a.__mul__, NAN)
393 self.assertRaises(ValueError, a.__truediv__, NAN)
Tim Peters2a799bf2002-12-16 20:18:38 +0000394
395 def test_basic_attributes(self):
396 days, seconds, us = 1, 7, 31
397 td = timedelta(days, seconds, us)
398 self.assertEqual(td.days, days)
399 self.assertEqual(td.seconds, seconds)
400 self.assertEqual(td.microseconds, us)
401
Antoine Pitroube6859d2009-11-25 23:02:32 +0000402 def test_total_seconds(self):
403 td = timedelta(days=365)
404 self.assertEqual(td.total_seconds(), 31536000.0)
405 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
406 td = timedelta(seconds=total_seconds)
407 self.assertEqual(td.total_seconds(), total_seconds)
Mark Dickinson0381e3f2010-05-08 14:35:02 +0000408 # Issue8644: Test that td.total_seconds() has the same
409 # accuracy as td / timedelta(seconds=1).
410 for ms in [-1, -2, -123]:
411 td = timedelta(microseconds=ms)
412 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
Antoine Pitroube6859d2009-11-25 23:02:32 +0000413
Tim Peters2a799bf2002-12-16 20:18:38 +0000414 def test_carries(self):
415 t1 = timedelta(days=100,
416 weeks=-7,
417 hours=-24*(100-49),
418 minutes=-3,
419 seconds=12,
420 microseconds=(3*60 - 12) * 1e6 + 1)
421 t2 = timedelta(microseconds=1)
422 self.assertEqual(t1, t2)
423
424 def test_hash_equality(self):
425 t1 = timedelta(days=100,
426 weeks=-7,
427 hours=-24*(100-49),
428 minutes=-3,
429 seconds=12,
430 microseconds=(3*60 - 12) * 1000000)
431 t2 = timedelta()
432 self.assertEqual(hash(t1), hash(t2))
433
434 t1 += timedelta(weeks=7)
435 t2 += timedelta(days=7*7)
436 self.assertEqual(t1, t2)
437 self.assertEqual(hash(t1), hash(t2))
438
439 d = {t1: 1}
440 d[t2] = 2
441 self.assertEqual(len(d), 1)
442 self.assertEqual(d[t1], 2)
443
444 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +0000445 args = 12, 34, 56
446 orig = timedelta(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +0000447 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +0000448 green = pickler.dumps(orig, proto)
449 derived = unpickler.loads(green)
450 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +0000451
452 def test_compare(self):
453 t1 = timedelta(2, 3, 4)
454 t2 = timedelta(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +0000455 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000456 self.assertTrue(t1 <= t2)
457 self.assertTrue(t1 >= t2)
458 self.assertTrue(not t1 != t2)
459 self.assertTrue(not t1 < t2)
460 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +0000461
462 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
463 t2 = timedelta(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000464 self.assertTrue(t1 < t2)
465 self.assertTrue(t2 > t1)
466 self.assertTrue(t1 <= t2)
467 self.assertTrue(t2 >= t1)
468 self.assertTrue(t1 != t2)
469 self.assertTrue(t2 != t1)
470 self.assertTrue(not t1 == t2)
471 self.assertTrue(not t2 == t1)
472 self.assertTrue(not t1 > t2)
473 self.assertTrue(not t2 < t1)
474 self.assertTrue(not t1 >= t2)
475 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000476
Tim Peters68124bb2003-02-08 03:46:31 +0000477 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +0000478 self.assertEqual(t1 == badarg, False)
479 self.assertEqual(t1 != badarg, True)
480 self.assertEqual(badarg == t1, False)
481 self.assertEqual(badarg != t1, True)
482
Tim Peters2a799bf2002-12-16 20:18:38 +0000483 self.assertRaises(TypeError, lambda: t1 <= badarg)
484 self.assertRaises(TypeError, lambda: t1 < badarg)
485 self.assertRaises(TypeError, lambda: t1 > badarg)
486 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +0000487 self.assertRaises(TypeError, lambda: badarg <= t1)
488 self.assertRaises(TypeError, lambda: badarg < t1)
489 self.assertRaises(TypeError, lambda: badarg > t1)
490 self.assertRaises(TypeError, lambda: badarg >= t1)
491
492 def test_str(self):
493 td = timedelta
494 eq = self.assertEqual
495
496 eq(str(td(1)), "1 day, 0:00:00")
497 eq(str(td(-1)), "-1 day, 0:00:00")
498 eq(str(td(2)), "2 days, 0:00:00")
499 eq(str(td(-2)), "-2 days, 0:00:00")
500
501 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
502 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
503 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
504 "-210 days, 23:12:34")
505
506 eq(str(td(milliseconds=1)), "0:00:00.001000")
507 eq(str(td(microseconds=3)), "0:00:00.000003")
508
509 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
510 microseconds=999999)),
511 "999999999 days, 23:59:59.999999")
512
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000513 def test_repr(self):
514 name = 'datetime.' + self.theclass.__name__
515 self.assertEqual(repr(self.theclass(1)),
516 "%s(1)" % name)
517 self.assertEqual(repr(self.theclass(10, 2)),
518 "%s(10, 2)" % name)
519 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
520 "%s(-10, 2, 400000)" % name)
521
Tim Peters2a799bf2002-12-16 20:18:38 +0000522 def test_roundtrip(self):
523 for td in (timedelta(days=999999999, hours=23, minutes=59,
524 seconds=59, microseconds=999999),
525 timedelta(days=-999999999),
Alexander Belopolsky1b402922010-06-22 14:07:33 +0000526 timedelta(days=-999999999, seconds=1),
Tim Peters2a799bf2002-12-16 20:18:38 +0000527 timedelta(days=1, seconds=2, microseconds=3)):
528
529 # Verify td -> string -> td identity.
530 s = repr(td)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000531 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000532 s = s[9:]
533 td2 = eval(s)
534 self.assertEqual(td, td2)
535
536 # Verify identity via reconstructing from pieces.
537 td2 = timedelta(td.days, td.seconds, td.microseconds)
538 self.assertEqual(td, td2)
539
540 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +0000541 self.assertIsInstance(timedelta.min, timedelta)
542 self.assertIsInstance(timedelta.max, timedelta)
543 self.assertIsInstance(timedelta.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000544 self.assertTrue(timedelta.max > timedelta.min)
Tim Peters2a799bf2002-12-16 20:18:38 +0000545 self.assertEqual(timedelta.min, timedelta(-999999999))
546 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
547 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
548
549 def test_overflow(self):
550 tiny = timedelta.resolution
551
552 td = timedelta.min + tiny
553 td -= tiny # no problem
554 self.assertRaises(OverflowError, td.__sub__, tiny)
555 self.assertRaises(OverflowError, td.__add__, -tiny)
556
557 td = timedelta.max - tiny
558 td += tiny # no problem
559 self.assertRaises(OverflowError, td.__add__, tiny)
560 self.assertRaises(OverflowError, td.__sub__, -tiny)
561
562 self.assertRaises(OverflowError, lambda: -timedelta.max)
563
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000564 day = timedelta(1)
565 self.assertRaises(OverflowError, day.__mul__, 10**9)
566 self.assertRaises(OverflowError, day.__mul__, 1e9)
567 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
568 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
569 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
570
571 @requires_IEEE_754
572 def _test_overflow_special(self):
573 day = timedelta(1)
574 self.assertRaises(OverflowError, day.__mul__, INF)
575 self.assertRaises(OverflowError, day.__mul__, -INF)
576
Tim Peters2a799bf2002-12-16 20:18:38 +0000577 def test_microsecond_rounding(self):
578 td = timedelta
579 eq = self.assertEqual
580
581 # Single-field rounding.
582 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
583 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
584 eq(td(milliseconds=0.6/1000), td(microseconds=1))
585 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
586
587 # Rounding due to contributions from more than one field.
588 us_per_hour = 3600e6
589 us_per_day = us_per_hour * 24
590 eq(td(days=.4/us_per_day), td(0))
591 eq(td(hours=.2/us_per_hour), td(0))
592 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
593
594 eq(td(days=-.4/us_per_day), td(0))
595 eq(td(hours=-.2/us_per_hour), td(0))
596 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
597
598 def test_massive_normalization(self):
599 td = timedelta(microseconds=-1)
600 self.assertEqual((td.days, td.seconds, td.microseconds),
601 (-1, 24*3600-1, 999999))
602
603 def test_bool(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000604 self.assertTrue(timedelta(1))
605 self.assertTrue(timedelta(0, 1))
606 self.assertTrue(timedelta(0, 0, 1))
607 self.assertTrue(timedelta(microseconds=1))
608 self.assertTrue(not timedelta(0))
Tim Peters2a799bf2002-12-16 20:18:38 +0000609
Tim Petersb0c854d2003-05-17 15:57:00 +0000610 def test_subclass_timedelta(self):
611
612 class T(timedelta):
Guido van Rossum5a8a0372005-01-16 00:25:31 +0000613 @staticmethod
Tim Petersb0c854d2003-05-17 15:57:00 +0000614 def from_td(td):
615 return T(td.days, td.seconds, td.microseconds)
Tim Petersb0c854d2003-05-17 15:57:00 +0000616
617 def as_hours(self):
618 sum = (self.days * 24 +
619 self.seconds / 3600.0 +
620 self.microseconds / 3600e6)
621 return round(sum)
622
623 t1 = T(days=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000624 self.assertTrue(type(t1) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000625 self.assertEqual(t1.as_hours(), 24)
626
627 t2 = T(days=-1, seconds=-3600)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000628 self.assertTrue(type(t2) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000629 self.assertEqual(t2.as_hours(), -25)
630
631 t3 = t1 + t2
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000632 self.assertTrue(type(t3) is timedelta)
Tim Petersb0c854d2003-05-17 15:57:00 +0000633 t4 = T.from_td(t3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000634 self.assertTrue(type(t4) is T)
Tim Petersb0c854d2003-05-17 15:57:00 +0000635 self.assertEqual(t3.days, t4.days)
636 self.assertEqual(t3.seconds, t4.seconds)
637 self.assertEqual(t3.microseconds, t4.microseconds)
638 self.assertEqual(str(t3), str(t4))
639 self.assertEqual(t4.as_hours(), -1)
640
Mark Dickinson7c186e22010-04-20 22:32:49 +0000641 def test_division(self):
642 t = timedelta(hours=1, minutes=24, seconds=19)
643 second = timedelta(seconds=1)
644 self.assertEqual(t / second, 5059.0)
645 self.assertEqual(t // second, 5059)
646
647 t = timedelta(minutes=2, seconds=30)
648 minute = timedelta(minutes=1)
649 self.assertEqual(t / minute, 2.5)
650 self.assertEqual(t // minute, 2)
651
652 zerotd = timedelta(0)
653 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
654 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
655
Alexander Belopolsky1790bc42010-05-31 17:33:47 +0000656 # self.assertRaises(TypeError, truediv, t, 2)
Mark Dickinson7c186e22010-04-20 22:32:49 +0000657 # note: floor division of a timedelta by an integer *is*
658 # currently permitted.
659
660 def test_remainder(self):
661 t = timedelta(minutes=2, seconds=30)
662 minute = timedelta(minutes=1)
663 r = t % minute
664 self.assertEqual(r, timedelta(seconds=30))
665
666 t = timedelta(minutes=-2, seconds=30)
667 r = t % minute
668 self.assertEqual(r, timedelta(seconds=30))
669
670 zerotd = timedelta(0)
671 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
672
673 self.assertRaises(TypeError, mod, t, 10)
674
675 def test_divmod(self):
676 t = timedelta(minutes=2, seconds=30)
677 minute = timedelta(minutes=1)
678 q, r = divmod(t, minute)
679 self.assertEqual(q, 2)
680 self.assertEqual(r, timedelta(seconds=30))
681
682 t = timedelta(minutes=-2, seconds=30)
683 q, r = divmod(t, minute)
684 self.assertEqual(q, -2)
685 self.assertEqual(r, timedelta(seconds=30))
686
687 zerotd = timedelta(0)
688 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
689
690 self.assertRaises(TypeError, divmod, t, 10)
691
692
Tim Peters2a799bf2002-12-16 20:18:38 +0000693#############################################################################
694# date tests
695
696class TestDateOnly(unittest.TestCase):
697 # Tests here won't pass if also run on datetime objects, so don't
698 # subclass this to test datetimes too.
699
700 def test_delta_non_days_ignored(self):
701 dt = date(2000, 1, 2)
702 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
703 microseconds=5)
704 days = timedelta(delta.days)
705 self.assertEqual(days, timedelta(1))
706
707 dt2 = dt + delta
708 self.assertEqual(dt2, dt + days)
709
710 dt2 = delta + dt
711 self.assertEqual(dt2, dt + days)
712
713 dt2 = dt - delta
714 self.assertEqual(dt2, dt - days)
715
716 delta = -delta
717 days = timedelta(delta.days)
718 self.assertEqual(days, timedelta(-2))
719
720 dt2 = dt + delta
721 self.assertEqual(dt2, dt + days)
722
723 dt2 = delta + dt
724 self.assertEqual(dt2, dt + days)
725
726 dt2 = dt - delta
727 self.assertEqual(dt2, dt - days)
728
Tim Peters604c0132004-06-07 23:04:33 +0000729class SubclassDate(date):
730 sub_var = 1
731
Guido van Rossumd8faa362007-04-27 19:54:29 +0000732class TestDate(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +0000733 # Tests here should pass for both dates and datetimes, except for a
734 # few tests that TestDateTime overrides.
735
736 theclass = date
737
738 def test_basic_attributes(self):
739 dt = self.theclass(2002, 3, 1)
740 self.assertEqual(dt.year, 2002)
741 self.assertEqual(dt.month, 3)
742 self.assertEqual(dt.day, 1)
743
744 def test_roundtrip(self):
745 for dt in (self.theclass(1, 2, 3),
746 self.theclass.today()):
747 # Verify dt -> string -> date identity.
748 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000749 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +0000750 s = s[9:]
751 dt2 = eval(s)
752 self.assertEqual(dt, dt2)
753
754 # Verify identity via reconstructing from pieces.
755 dt2 = self.theclass(dt.year, dt.month, dt.day)
756 self.assertEqual(dt, dt2)
757
758 def test_ordinal_conversions(self):
759 # Check some fixed values.
760 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
761 (1, 12, 31, 365),
762 (2, 1, 1, 366),
763 # first example from "Calendrical Calculations"
764 (1945, 11, 12, 710347)]:
765 d = self.theclass(y, m, d)
766 self.assertEqual(n, d.toordinal())
767 fromord = self.theclass.fromordinal(n)
768 self.assertEqual(d, fromord)
769 if hasattr(fromord, "hour"):
Tim Petersf2715e02003-02-19 02:35:07 +0000770 # if we're checking something fancier than a date, verify
771 # the extra fields have been zeroed out
Tim Peters2a799bf2002-12-16 20:18:38 +0000772 self.assertEqual(fromord.hour, 0)
773 self.assertEqual(fromord.minute, 0)
774 self.assertEqual(fromord.second, 0)
775 self.assertEqual(fromord.microsecond, 0)
776
Tim Peters0bf60bd2003-01-08 20:40:01 +0000777 # Check first and last days of year spottily across the whole
778 # range of years supported.
Guido van Rossum805365e2007-05-07 22:24:25 +0000779 for year in range(MINYEAR, MAXYEAR+1, 7):
Tim Peters2a799bf2002-12-16 20:18:38 +0000780 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
781 d = self.theclass(year, 1, 1)
782 n = d.toordinal()
783 d2 = self.theclass.fromordinal(n)
784 self.assertEqual(d, d2)
Tim Peters0bf60bd2003-01-08 20:40:01 +0000785 # Verify that moving back a day gets to the end of year-1.
786 if year > 1:
787 d = self.theclass.fromordinal(n-1)
788 d2 = self.theclass(year-1, 12, 31)
789 self.assertEqual(d, d2)
790 self.assertEqual(d2.toordinal(), n-1)
Tim Peters2a799bf2002-12-16 20:18:38 +0000791
792 # Test every day in a leap-year and a non-leap year.
793 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
794 for year, isleap in (2000, True), (2002, False):
795 n = self.theclass(year, 1, 1).toordinal()
796 for month, maxday in zip(range(1, 13), dim):
797 if month == 2 and isleap:
798 maxday += 1
799 for day in range(1, maxday+1):
800 d = self.theclass(year, month, day)
801 self.assertEqual(d.toordinal(), n)
802 self.assertEqual(d, self.theclass.fromordinal(n))
803 n += 1
804
805 def test_extreme_ordinals(self):
806 a = self.theclass.min
807 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
808 aord = a.toordinal()
809 b = a.fromordinal(aord)
810 self.assertEqual(a, b)
811
812 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
813
814 b = a + timedelta(days=1)
815 self.assertEqual(b.toordinal(), aord + 1)
816 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
817
818 a = self.theclass.max
819 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
820 aord = a.toordinal()
821 b = a.fromordinal(aord)
822 self.assertEqual(a, b)
823
824 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
825
826 b = a - timedelta(days=1)
827 self.assertEqual(b.toordinal(), aord - 1)
828 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
829
830 def test_bad_constructor_arguments(self):
831 # bad years
832 self.theclass(MINYEAR, 1, 1) # no exception
833 self.theclass(MAXYEAR, 1, 1) # no exception
834 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
835 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
836 # bad months
837 self.theclass(2000, 1, 1) # no exception
838 self.theclass(2000, 12, 1) # no exception
839 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
840 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
841 # bad days
842 self.theclass(2000, 2, 29) # no exception
843 self.theclass(2004, 2, 29) # no exception
844 self.theclass(2400, 2, 29) # no exception
845 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
846 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
847 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
848 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
849 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
850 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
851
852 def test_hash_equality(self):
853 d = self.theclass(2000, 12, 31)
854 # same thing
855 e = self.theclass(2000, 12, 31)
856 self.assertEqual(d, e)
857 self.assertEqual(hash(d), hash(e))
858
859 dic = {d: 1}
860 dic[e] = 2
861 self.assertEqual(len(dic), 1)
862 self.assertEqual(dic[d], 2)
863 self.assertEqual(dic[e], 2)
864
865 d = self.theclass(2001, 1, 1)
866 # same thing
867 e = self.theclass(2001, 1, 1)
868 self.assertEqual(d, e)
869 self.assertEqual(hash(d), hash(e))
870
871 dic = {d: 1}
872 dic[e] = 2
873 self.assertEqual(len(dic), 1)
874 self.assertEqual(dic[d], 2)
875 self.assertEqual(dic[e], 2)
876
877 def test_computations(self):
878 a = self.theclass(2002, 1, 31)
879 b = self.theclass(1956, 1, 31)
880
881 diff = a-b
882 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
883 self.assertEqual(diff.seconds, 0)
884 self.assertEqual(diff.microseconds, 0)
885
886 day = timedelta(1)
887 week = timedelta(7)
888 a = self.theclass(2002, 3, 2)
889 self.assertEqual(a + day, self.theclass(2002, 3, 3))
890 self.assertEqual(day + a, self.theclass(2002, 3, 3))
891 self.assertEqual(a - day, self.theclass(2002, 3, 1))
892 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
893 self.assertEqual(a + week, self.theclass(2002, 3, 9))
894 self.assertEqual(a - week, self.theclass(2002, 2, 23))
895 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
896 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
897 self.assertEqual((a + week) - a, week)
898 self.assertEqual((a + day) - a, day)
899 self.assertEqual((a - week) - a, -week)
900 self.assertEqual((a - day) - a, -day)
901 self.assertEqual(a - (a + week), -week)
902 self.assertEqual(a - (a + day), -day)
903 self.assertEqual(a - (a - week), week)
904 self.assertEqual(a - (a - day), day)
905
Mark Dickinson5c2db372009-12-05 20:28:34 +0000906 # Add/sub ints or floats should be illegal
907 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +0000908 self.assertRaises(TypeError, lambda: a+i)
909 self.assertRaises(TypeError, lambda: a-i)
910 self.assertRaises(TypeError, lambda: i+a)
911 self.assertRaises(TypeError, lambda: i-a)
912
913 # delta - date is senseless.
914 self.assertRaises(TypeError, lambda: day - a)
915 # mixing date and (delta or date) via * or // is senseless
916 self.assertRaises(TypeError, lambda: day * a)
917 self.assertRaises(TypeError, lambda: a * day)
918 self.assertRaises(TypeError, lambda: day // a)
919 self.assertRaises(TypeError, lambda: a // day)
920 self.assertRaises(TypeError, lambda: a * a)
921 self.assertRaises(TypeError, lambda: a // a)
922 # date + date is senseless
923 self.assertRaises(TypeError, lambda: a + a)
924
925 def test_overflow(self):
926 tiny = self.theclass.resolution
927
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000928 for delta in [tiny, timedelta(1), timedelta(2)]:
929 dt = self.theclass.min + delta
930 dt -= delta # no problem
931 self.assertRaises(OverflowError, dt.__sub__, delta)
932 self.assertRaises(OverflowError, dt.__add__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000933
Alexander Belopolskyf03a6162010-05-27 21:42:58 +0000934 dt = self.theclass.max - delta
935 dt += delta # no problem
936 self.assertRaises(OverflowError, dt.__add__, delta)
937 self.assertRaises(OverflowError, dt.__sub__, -delta)
Tim Peters2a799bf2002-12-16 20:18:38 +0000938
939 def test_fromtimestamp(self):
940 import time
941
942 # Try an arbitrary fixed value.
943 year, month, day = 1999, 9, 19
944 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
945 d = self.theclass.fromtimestamp(ts)
946 self.assertEqual(d.year, year)
947 self.assertEqual(d.month, month)
948 self.assertEqual(d.day, day)
949
Tim Peters1b6f7a92004-06-20 02:50:16 +0000950 def test_insane_fromtimestamp(self):
951 # It's possible that some platform maps time_t to double,
952 # and that this test will fail there. This test should
953 # exempt such platforms (provided they return reasonable
954 # results!).
955 for insane in -1e200, 1e200:
956 self.assertRaises(ValueError, self.theclass.fromtimestamp,
957 insane)
958
Tim Peters2a799bf2002-12-16 20:18:38 +0000959 def test_today(self):
960 import time
961
962 # We claim that today() is like fromtimestamp(time.time()), so
963 # prove it.
964 for dummy in range(3):
965 today = self.theclass.today()
966 ts = time.time()
967 todayagain = self.theclass.fromtimestamp(ts)
968 if today == todayagain:
969 break
970 # There are several legit reasons that could fail:
971 # 1. It recently became midnight, between the today() and the
972 # time() calls.
973 # 2. The platform time() has such fine resolution that we'll
974 # never get the same value twice.
975 # 3. The platform time() has poor resolution, and we just
976 # happened to call today() right before a resolution quantum
977 # boundary.
978 # 4. The system clock got fiddled between calls.
979 # In any case, wait a little while and try again.
980 time.sleep(0.1)
981
982 # It worked or it didn't. If it didn't, assume it's reason #2, and
983 # let the test pass if they're within half a second of each other.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000984 self.assertTrue(today == todayagain or
Tim Peters2a799bf2002-12-16 20:18:38 +0000985 abs(todayagain - today) < timedelta(seconds=0.5))
986
987 def test_weekday(self):
988 for i in range(7):
989 # March 4, 2002 is a Monday
990 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
991 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
992 # January 2, 1956 is a Monday
993 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
994 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
995
996 def test_isocalendar(self):
997 # Check examples from
998 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
999 for i in range(7):
1000 d = self.theclass(2003, 12, 22+i)
1001 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1002 d = self.theclass(2003, 12, 29) + timedelta(i)
1003 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1004 d = self.theclass(2004, 1, 5+i)
1005 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1006 d = self.theclass(2009, 12, 21+i)
1007 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1008 d = self.theclass(2009, 12, 28) + timedelta(i)
1009 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1010 d = self.theclass(2010, 1, 4+i)
1011 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1012
1013 def test_iso_long_years(self):
1014 # Calculate long ISO years and compare to table from
1015 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1016 ISO_LONG_YEARS_TABLE = """
1017 4 32 60 88
1018 9 37 65 93
1019 15 43 71 99
1020 20 48 76
1021 26 54 82
1022
1023 105 133 161 189
1024 111 139 167 195
1025 116 144 172
1026 122 150 178
1027 128 156 184
1028
1029 201 229 257 285
1030 207 235 263 291
1031 212 240 268 296
1032 218 246 274
1033 224 252 280
1034
1035 303 331 359 387
1036 308 336 364 392
1037 314 342 370 398
1038 320 348 376
1039 325 353 381
1040 """
Guido van Rossumc1f779c2007-07-03 08:25:58 +00001041 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
Tim Peters2a799bf2002-12-16 20:18:38 +00001042 L = []
1043 for i in range(400):
1044 d = self.theclass(2000+i, 12, 31)
1045 d1 = self.theclass(1600+i, 12, 31)
1046 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1047 if d.isocalendar()[1] == 53:
1048 L.append(i)
1049 self.assertEqual(L, iso_long_years)
1050
1051 def test_isoformat(self):
1052 t = self.theclass(2, 3, 2)
1053 self.assertEqual(t.isoformat(), "0002-03-02")
1054
1055 def test_ctime(self):
1056 t = self.theclass(2002, 3, 2)
1057 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1058
1059 def test_strftime(self):
1060 t = self.theclass(2005, 3, 2)
1061 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
Raymond Hettingerf69d9f62003-06-27 08:14:17 +00001062 self.assertEqual(t.strftime(""), "") # SF bug #761337
Thomas Wouters89f507f2006-12-13 04:49:30 +00001063 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
Tim Peters2a799bf2002-12-16 20:18:38 +00001064
1065 self.assertRaises(TypeError, t.strftime) # needs an arg
1066 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1067 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1068
Georg Brandlf78e02b2008-06-10 17:40:04 +00001069 # test that unicode input is allowed (issue 2782)
1070 self.assertEqual(t.strftime("%m"), "03")
1071
Tim Peters2a799bf2002-12-16 20:18:38 +00001072 # A naive object replaces %z and %Z w/ empty strings.
1073 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1074
Benjamin Petersone1cdfd72009-01-18 21:02:37 +00001075 #make sure that invalid format specifiers are handled correctly
1076 #self.assertRaises(ValueError, t.strftime, "%e")
1077 #self.assertRaises(ValueError, t.strftime, "%")
1078 #self.assertRaises(ValueError, t.strftime, "%#")
1079
1080 #oh well, some systems just ignore those invalid ones.
1081 #at least, excercise them to make sure that no crashes
1082 #are generated
1083 for f in ["%e", "%", "%#"]:
1084 try:
1085 t.strftime(f)
1086 except ValueError:
1087 pass
1088
1089 #check that this standard extension works
1090 t.strftime("%f")
1091
Georg Brandlf78e02b2008-06-10 17:40:04 +00001092
Eric Smith1ba31142007-09-11 18:06:02 +00001093 def test_format(self):
1094 dt = self.theclass(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001095 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001096
1097 # check that a derived class's __str__() gets called
1098 class A(self.theclass):
1099 def __str__(self):
1100 return 'A'
1101 a = A(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001102 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001103
1104 # check that a derived class's strftime gets called
1105 class B(self.theclass):
1106 def strftime(self, format_spec):
1107 return 'B'
1108 b = B(2007, 9, 10)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001109 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001110
1111 for fmt in ["m:%m d:%d y:%y",
1112 "m:%m d:%d y:%y H:%H M:%M S:%S",
1113 "%z %Z",
1114 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001115 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1116 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1117 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001118
Tim Peters2a799bf2002-12-16 20:18:38 +00001119 def test_resolution_info(self):
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001120 # XXX: Should min and max respect subclassing?
1121 if issubclass(self.theclass, datetime):
1122 expected_class = datetime
1123 else:
1124 expected_class = date
1125 self.assertIsInstance(self.theclass.min, expected_class)
1126 self.assertIsInstance(self.theclass.max, expected_class)
Ezio Melottie9615932010-01-24 19:26:24 +00001127 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001128 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00001129
1130 def test_extreme_timedelta(self):
1131 big = self.theclass.max - self.theclass.min
1132 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1133 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1134 # n == 315537897599999999 ~= 2**58.13
1135 justasbig = timedelta(0, 0, n)
1136 self.assertEqual(big, justasbig)
1137 self.assertEqual(self.theclass.min + big, self.theclass.max)
1138 self.assertEqual(self.theclass.max - big, self.theclass.min)
1139
1140 def test_timetuple(self):
1141 for i in range(7):
1142 # January 2, 1956 is a Monday (0)
1143 d = self.theclass(1956, 1, 2+i)
1144 t = d.timetuple()
1145 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1146 # February 1, 1956 is a Wednesday (2)
1147 d = self.theclass(1956, 2, 1+i)
1148 t = d.timetuple()
1149 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1150 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1151 # of the year.
1152 d = self.theclass(1956, 3, 1+i)
1153 t = d.timetuple()
1154 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1155 self.assertEqual(t.tm_year, 1956)
1156 self.assertEqual(t.tm_mon, 3)
1157 self.assertEqual(t.tm_mday, 1+i)
1158 self.assertEqual(t.tm_hour, 0)
1159 self.assertEqual(t.tm_min, 0)
1160 self.assertEqual(t.tm_sec, 0)
1161 self.assertEqual(t.tm_wday, (3+i)%7)
1162 self.assertEqual(t.tm_yday, 61+i)
1163 self.assertEqual(t.tm_isdst, -1)
1164
1165 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001166 args = 6, 7, 23
1167 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001168 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001169 green = pickler.dumps(orig, proto)
1170 derived = unpickler.loads(green)
1171 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001172
1173 def test_compare(self):
1174 t1 = self.theclass(2, 3, 4)
1175 t2 = self.theclass(2, 3, 4)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001176 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001177 self.assertTrue(t1 <= t2)
1178 self.assertTrue(t1 >= t2)
1179 self.assertTrue(not t1 != t2)
1180 self.assertTrue(not t1 < t2)
1181 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001182
1183 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1184 t2 = self.theclass(*args) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001185 self.assertTrue(t1 < t2)
1186 self.assertTrue(t2 > t1)
1187 self.assertTrue(t1 <= t2)
1188 self.assertTrue(t2 >= t1)
1189 self.assertTrue(t1 != t2)
1190 self.assertTrue(t2 != t1)
1191 self.assertTrue(not t1 == t2)
1192 self.assertTrue(not t2 == t1)
1193 self.assertTrue(not t1 > t2)
1194 self.assertTrue(not t2 < t1)
1195 self.assertTrue(not t1 >= t2)
1196 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001197
Tim Peters68124bb2003-02-08 03:46:31 +00001198 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001199 self.assertEqual(t1 == badarg, False)
1200 self.assertEqual(t1 != badarg, True)
1201 self.assertEqual(badarg == t1, False)
1202 self.assertEqual(badarg != t1, True)
1203
Tim Peters2a799bf2002-12-16 20:18:38 +00001204 self.assertRaises(TypeError, lambda: t1 < badarg)
1205 self.assertRaises(TypeError, lambda: t1 > badarg)
1206 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00001207 self.assertRaises(TypeError, lambda: badarg <= t1)
1208 self.assertRaises(TypeError, lambda: badarg < t1)
1209 self.assertRaises(TypeError, lambda: badarg > t1)
1210 self.assertRaises(TypeError, lambda: badarg >= t1)
1211
Tim Peters8d81a012003-01-24 22:36:34 +00001212 def test_mixed_compare(self):
1213 our = self.theclass(2000, 4, 5)
Guido van Rossum19960592006-08-24 17:29:38 +00001214
1215 # Our class can be compared for equality to other classes
1216 self.assertEqual(our == 1, False)
1217 self.assertEqual(1 == our, False)
1218 self.assertEqual(our != 1, True)
1219 self.assertEqual(1 != our, True)
1220
1221 # But the ordering is undefined
1222 self.assertRaises(TypeError, lambda: our < 1)
1223 self.assertRaises(TypeError, lambda: 1 < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001224
Guido van Rossum19960592006-08-24 17:29:38 +00001225 # Repeat those tests with a different class
Tim Peters8d81a012003-01-24 22:36:34 +00001226
Guido van Rossum19960592006-08-24 17:29:38 +00001227 class SomeClass:
1228 pass
1229
1230 their = SomeClass()
1231 self.assertEqual(our == their, False)
1232 self.assertEqual(their == our, False)
1233 self.assertEqual(our != their, True)
1234 self.assertEqual(their != our, True)
1235 self.assertRaises(TypeError, lambda: our < their)
1236 self.assertRaises(TypeError, lambda: their < our)
Tim Peters8d81a012003-01-24 22:36:34 +00001237
Guido van Rossum19960592006-08-24 17:29:38 +00001238 # However, if the other class explicitly defines ordering
1239 # relative to our class, it is allowed to do so
Tim Peters8d81a012003-01-24 22:36:34 +00001240
Guido van Rossum19960592006-08-24 17:29:38 +00001241 class LargerThanAnything:
1242 def __lt__(self, other):
1243 return False
1244 def __le__(self, other):
1245 return isinstance(other, LargerThanAnything)
1246 def __eq__(self, other):
1247 return isinstance(other, LargerThanAnything)
1248 def __ne__(self, other):
1249 return not isinstance(other, LargerThanAnything)
1250 def __gt__(self, other):
1251 return not isinstance(other, LargerThanAnything)
1252 def __ge__(self, other):
1253 return True
1254
1255 their = LargerThanAnything()
1256 self.assertEqual(our == their, False)
1257 self.assertEqual(their == our, False)
1258 self.assertEqual(our != their, True)
1259 self.assertEqual(their != our, True)
1260 self.assertEqual(our < their, True)
1261 self.assertEqual(their < our, False)
Tim Peters8d81a012003-01-24 22:36:34 +00001262
Tim Peters2a799bf2002-12-16 20:18:38 +00001263 def test_bool(self):
1264 # All dates are considered true.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001265 self.assertTrue(self.theclass.min)
1266 self.assertTrue(self.theclass.max)
Tim Peters2a799bf2002-12-16 20:18:38 +00001267
Guido van Rossum04110fb2007-08-24 16:32:05 +00001268 def test_strftime_out_of_range(self):
Tim Petersd6844152002-12-22 20:58:42 +00001269 # For nasty technical reasons, we can't handle years before 1900.
1270 cls = self.theclass
1271 self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
1272 for y in 1, 49, 51, 99, 100, 1000, 1899:
1273 self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
Tim Peters12bf3392002-12-24 05:41:27 +00001274
1275 def test_replace(self):
1276 cls = self.theclass
1277 args = [1, 2, 3]
1278 base = cls(*args)
1279 self.assertEqual(base, base.replace())
1280
1281 i = 0
1282 for name, newval in (("year", 2),
1283 ("month", 3),
1284 ("day", 4)):
1285 newargs = args[:]
1286 newargs[i] = newval
1287 expected = cls(*newargs)
1288 got = base.replace(**{name: newval})
1289 self.assertEqual(expected, got)
1290 i += 1
1291
1292 # Out of bounds.
1293 base = cls(2000, 2, 29)
1294 self.assertRaises(ValueError, base.replace, year=2001)
1295
Tim Petersa98924a2003-05-17 05:55:19 +00001296 def test_subclass_date(self):
1297
1298 class C(self.theclass):
1299 theAnswer = 42
1300
1301 def __new__(cls, *args, **kws):
1302 temp = kws.copy()
1303 extra = temp.pop('extra')
1304 result = self.theclass.__new__(cls, *args, **temp)
1305 result.extra = extra
1306 return result
1307
1308 def newmeth(self, start):
1309 return start + self.year + self.month
1310
1311 args = 2003, 4, 14
1312
1313 dt1 = self.theclass(*args)
1314 dt2 = C(*args, **{'extra': 7})
1315
1316 self.assertEqual(dt2.__class__, C)
1317 self.assertEqual(dt2.theAnswer, 42)
1318 self.assertEqual(dt2.extra, 7)
1319 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1320 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1321
Tim Peters604c0132004-06-07 23:04:33 +00001322 def test_pickling_subclass_date(self):
1323
1324 args = 6, 7, 23
1325 orig = SubclassDate(*args)
1326 for pickler, unpickler, proto in pickle_choices:
1327 green = pickler.dumps(orig, proto)
1328 derived = unpickler.loads(green)
1329 self.assertEqual(orig, derived)
1330
Tim Peters3f606292004-03-21 23:38:41 +00001331 def test_backdoor_resistance(self):
Guido van Rossum254348e2007-11-21 19:29:53 +00001332 # For fast unpickling, the constructor accepts a pickle byte string.
Tim Peters3f606292004-03-21 23:38:41 +00001333 # This is a low-overhead backdoor. A user can (by intent or
1334 # mistake) pass a string directly, which (if it's the right length)
1335 # will get treated like a pickle, and bypass the normal sanity
1336 # checks in the constructor. This can create insane objects.
1337 # The constructor doesn't want to burn the time to validate all
1338 # fields, but does check the month field. This stops, e.g.,
1339 # datetime.datetime('1995-03-25') from yielding an insane object.
Guido van Rossum254348e2007-11-21 19:29:53 +00001340 base = b'1995-03-25'
Tim Peters3f606292004-03-21 23:38:41 +00001341 if not issubclass(self.theclass, datetime):
1342 base = base[:4]
Guido van Rossum254348e2007-11-21 19:29:53 +00001343 for month_byte in b'9', b'\0', b'\r', b'\xff':
Tim Peters3f606292004-03-21 23:38:41 +00001344 self.assertRaises(TypeError, self.theclass,
1345 base[:2] + month_byte + base[3:])
1346 for ord_byte in range(1, 13):
1347 # This shouldn't blow up because of the month byte alone. If
1348 # the implementation changes to do more-careful checking, it may
1349 # blow up because other fields are insane.
Guido van Rossum254348e2007-11-21 19:29:53 +00001350 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
Tim Peterseb1a4962003-05-17 02:25:20 +00001351
Tim Peters2a799bf2002-12-16 20:18:38 +00001352#############################################################################
1353# datetime tests
1354
Tim Peters604c0132004-06-07 23:04:33 +00001355class SubclassDatetime(datetime):
1356 sub_var = 1
1357
Tim Peters2a799bf2002-12-16 20:18:38 +00001358class TestDateTime(TestDate):
1359
1360 theclass = datetime
1361
1362 def test_basic_attributes(self):
1363 dt = self.theclass(2002, 3, 1, 12, 0)
1364 self.assertEqual(dt.year, 2002)
1365 self.assertEqual(dt.month, 3)
1366 self.assertEqual(dt.day, 1)
1367 self.assertEqual(dt.hour, 12)
1368 self.assertEqual(dt.minute, 0)
1369 self.assertEqual(dt.second, 0)
1370 self.assertEqual(dt.microsecond, 0)
1371
1372 def test_basic_attributes_nonzero(self):
1373 # Make sure all attributes are non-zero so bugs in
1374 # bit-shifting access show up.
1375 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1376 self.assertEqual(dt.year, 2002)
1377 self.assertEqual(dt.month, 3)
1378 self.assertEqual(dt.day, 1)
1379 self.assertEqual(dt.hour, 12)
1380 self.assertEqual(dt.minute, 59)
1381 self.assertEqual(dt.second, 59)
1382 self.assertEqual(dt.microsecond, 8000)
1383
1384 def test_roundtrip(self):
1385 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1386 self.theclass.now()):
1387 # Verify dt -> string -> datetime identity.
1388 s = repr(dt)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001389 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001390 s = s[9:]
1391 dt2 = eval(s)
1392 self.assertEqual(dt, dt2)
1393
1394 # Verify identity via reconstructing from pieces.
1395 dt2 = self.theclass(dt.year, dt.month, dt.day,
1396 dt.hour, dt.minute, dt.second,
1397 dt.microsecond)
1398 self.assertEqual(dt, dt2)
1399
1400 def test_isoformat(self):
1401 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1402 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1403 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1404 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
Amaury Forgeot d'Arcca5f1a72009-12-29 22:24:40 +00001405 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
Tim Peters2a799bf2002-12-16 20:18:38 +00001406 # str is ISO format with the separator forced to a blank.
1407 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1408
1409 t = self.theclass(2, 3, 2)
1410 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1411 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1412 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1413 # str is ISO format with the separator forced to a blank.
1414 self.assertEqual(str(t), "0002-03-02 00:00:00")
1415
Eric Smith1ba31142007-09-11 18:06:02 +00001416 def test_format(self):
1417 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001418 self.assertEqual(dt.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001419
1420 # check that a derived class's __str__() gets called
1421 class A(self.theclass):
1422 def __str__(self):
1423 return 'A'
1424 a = A(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001425 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00001426
1427 # check that a derived class's strftime gets called
1428 class B(self.theclass):
1429 def strftime(self, format_spec):
1430 return 'B'
1431 b = B(2007, 9, 10, 4, 5, 1, 123)
Eric Smith8fd3eba2008-02-17 19:48:00 +00001432 self.assertEqual(b.__format__(''), str(dt))
Eric Smith1ba31142007-09-11 18:06:02 +00001433
1434 for fmt in ["m:%m d:%d y:%y",
1435 "m:%m d:%d y:%y H:%H M:%M S:%S",
1436 "%z %Z",
1437 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00001438 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1439 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1440 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00001441
Tim Peters2a799bf2002-12-16 20:18:38 +00001442 def test_more_ctime(self):
1443 # Test fields that TestDate doesn't touch.
1444 import time
1445
1446 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1447 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1448 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1449 # out. The difference is that t.ctime() produces " 2" for the day,
1450 # but platform ctime() produces "02" for the day. According to
1451 # C99, t.ctime() is correct here.
1452 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1453
1454 # So test a case where that difference doesn't matter.
1455 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1456 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1457
1458 def test_tz_independent_comparing(self):
1459 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1460 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1461 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1462 self.assertEqual(dt1, dt3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001463 self.assertTrue(dt2 > dt3)
Tim Peters2a799bf2002-12-16 20:18:38 +00001464
1465 # Make sure comparison doesn't forget microseconds, and isn't done
1466 # via comparing a float timestamp (an IEEE double doesn't have enough
1467 # precision to span microsecond resolution across years 1 thru 9999,
1468 # so comparing via timestamp necessarily calls some distinct values
1469 # equal).
1470 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1471 us = timedelta(microseconds=1)
1472 dt2 = dt1 + us
1473 self.assertEqual(dt2 - dt1, us)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001474 self.assertTrue(dt1 < dt2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001475
Thomas Wouters49fd7fa2006-04-21 10:40:58 +00001476 def test_strftime_with_bad_tzname_replace(self):
1477 # verify ok if tzinfo.tzname().replace() returns a non-string
1478 class MyTzInfo(FixedOffset):
1479 def tzname(self, dt):
1480 class MyStr(str):
1481 def replace(self, *args):
1482 return None
1483 return MyStr('name')
1484 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1485 self.assertRaises(TypeError, t.strftime, '%Z')
1486
Tim Peters2a799bf2002-12-16 20:18:38 +00001487 def test_bad_constructor_arguments(self):
1488 # bad years
1489 self.theclass(MINYEAR, 1, 1) # no exception
1490 self.theclass(MAXYEAR, 1, 1) # no exception
1491 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1492 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1493 # bad months
1494 self.theclass(2000, 1, 1) # no exception
1495 self.theclass(2000, 12, 1) # no exception
1496 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1497 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1498 # bad days
1499 self.theclass(2000, 2, 29) # no exception
1500 self.theclass(2004, 2, 29) # no exception
1501 self.theclass(2400, 2, 29) # no exception
1502 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1503 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1504 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1505 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1506 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1507 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1508 # bad hours
1509 self.theclass(2000, 1, 31, 0) # no exception
1510 self.theclass(2000, 1, 31, 23) # no exception
1511 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1512 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1513 # bad minutes
1514 self.theclass(2000, 1, 31, 23, 0) # no exception
1515 self.theclass(2000, 1, 31, 23, 59) # no exception
1516 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1517 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1518 # bad seconds
1519 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1520 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1521 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1522 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1523 # bad microseconds
1524 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1525 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1526 self.assertRaises(ValueError, self.theclass,
1527 2000, 1, 31, 23, 59, 59, -1)
1528 self.assertRaises(ValueError, self.theclass,
1529 2000, 1, 31, 23, 59, 59,
1530 1000000)
1531
1532 def test_hash_equality(self):
1533 d = self.theclass(2000, 12, 31, 23, 30, 17)
1534 e = self.theclass(2000, 12, 31, 23, 30, 17)
1535 self.assertEqual(d, e)
1536 self.assertEqual(hash(d), hash(e))
1537
1538 dic = {d: 1}
1539 dic[e] = 2
1540 self.assertEqual(len(dic), 1)
1541 self.assertEqual(dic[d], 2)
1542 self.assertEqual(dic[e], 2)
1543
1544 d = self.theclass(2001, 1, 1, 0, 5, 17)
1545 e = self.theclass(2001, 1, 1, 0, 5, 17)
1546 self.assertEqual(d, e)
1547 self.assertEqual(hash(d), hash(e))
1548
1549 dic = {d: 1}
1550 dic[e] = 2
1551 self.assertEqual(len(dic), 1)
1552 self.assertEqual(dic[d], 2)
1553 self.assertEqual(dic[e], 2)
1554
1555 def test_computations(self):
1556 a = self.theclass(2002, 1, 31)
1557 b = self.theclass(1956, 1, 31)
1558 diff = a-b
1559 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1560 self.assertEqual(diff.seconds, 0)
1561 self.assertEqual(diff.microseconds, 0)
1562 a = self.theclass(2002, 3, 2, 17, 6)
1563 millisec = timedelta(0, 0, 1000)
1564 hour = timedelta(0, 3600)
1565 day = timedelta(1)
1566 week = timedelta(7)
1567 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1568 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1569 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1570 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1571 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1572 self.assertEqual(a - hour, a + -hour)
1573 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1574 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1575 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1576 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1577 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1578 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1579 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1580 self.assertEqual((a + week) - a, week)
1581 self.assertEqual((a + day) - a, day)
1582 self.assertEqual((a + hour) - a, hour)
1583 self.assertEqual((a + millisec) - a, millisec)
1584 self.assertEqual((a - week) - a, -week)
1585 self.assertEqual((a - day) - a, -day)
1586 self.assertEqual((a - hour) - a, -hour)
1587 self.assertEqual((a - millisec) - a, -millisec)
1588 self.assertEqual(a - (a + week), -week)
1589 self.assertEqual(a - (a + day), -day)
1590 self.assertEqual(a - (a + hour), -hour)
1591 self.assertEqual(a - (a + millisec), -millisec)
1592 self.assertEqual(a - (a - week), week)
1593 self.assertEqual(a - (a - day), day)
1594 self.assertEqual(a - (a - hour), hour)
1595 self.assertEqual(a - (a - millisec), millisec)
1596 self.assertEqual(a + (week + day + hour + millisec),
1597 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1598 self.assertEqual(a + (week + day + hour + millisec),
1599 (((a + week) + day) + hour) + millisec)
1600 self.assertEqual(a - (week + day + hour + millisec),
1601 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1602 self.assertEqual(a - (week + day + hour + millisec),
1603 (((a - week) - day) - hour) - millisec)
Mark Dickinson5c2db372009-12-05 20:28:34 +00001604 # Add/sub ints or floats should be illegal
1605 for i in 1, 1.0:
Tim Peters2a799bf2002-12-16 20:18:38 +00001606 self.assertRaises(TypeError, lambda: a+i)
1607 self.assertRaises(TypeError, lambda: a-i)
1608 self.assertRaises(TypeError, lambda: i+a)
1609 self.assertRaises(TypeError, lambda: i-a)
1610
1611 # delta - datetime is senseless.
1612 self.assertRaises(TypeError, lambda: day - a)
1613 # mixing datetime and (delta or datetime) via * or // is senseless
1614 self.assertRaises(TypeError, lambda: day * a)
1615 self.assertRaises(TypeError, lambda: a * day)
1616 self.assertRaises(TypeError, lambda: day // a)
1617 self.assertRaises(TypeError, lambda: a // day)
1618 self.assertRaises(TypeError, lambda: a * a)
1619 self.assertRaises(TypeError, lambda: a // a)
1620 # datetime + datetime is senseless
1621 self.assertRaises(TypeError, lambda: a + a)
1622
1623 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00001624 args = 6, 7, 23, 20, 59, 1, 64**2
1625 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00001626 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00001627 green = pickler.dumps(orig, proto)
1628 derived = unpickler.loads(green)
1629 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00001630
Guido van Rossum275666f2003-02-07 21:49:01 +00001631 def test_more_pickling(self):
1632 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1633 s = pickle.dumps(a)
1634 b = pickle.loads(s)
1635 self.assertEqual(b.year, 2003)
1636 self.assertEqual(b.month, 2)
1637 self.assertEqual(b.day, 7)
1638
Tim Peters604c0132004-06-07 23:04:33 +00001639 def test_pickling_subclass_datetime(self):
1640 args = 6, 7, 23, 20, 59, 1, 64**2
1641 orig = SubclassDatetime(*args)
1642 for pickler, unpickler, proto in pickle_choices:
1643 green = pickler.dumps(orig, proto)
1644 derived = unpickler.loads(green)
1645 self.assertEqual(orig, derived)
1646
Tim Peters2a799bf2002-12-16 20:18:38 +00001647 def test_more_compare(self):
1648 # The test_compare() inherited from TestDate covers the error cases.
1649 # We just want to test lexicographic ordering on the members datetime
1650 # has that date lacks.
1651 args = [2000, 11, 29, 20, 58, 16, 999998]
1652 t1 = self.theclass(*args)
1653 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001654 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001655 self.assertTrue(t1 <= t2)
1656 self.assertTrue(t1 >= t2)
1657 self.assertTrue(not t1 != t2)
1658 self.assertTrue(not t1 < t2)
1659 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001660
1661 for i in range(len(args)):
1662 newargs = args[:]
1663 newargs[i] = args[i] + 1
1664 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001665 self.assertTrue(t1 < t2)
1666 self.assertTrue(t2 > t1)
1667 self.assertTrue(t1 <= t2)
1668 self.assertTrue(t2 >= t1)
1669 self.assertTrue(t1 != t2)
1670 self.assertTrue(t2 != t1)
1671 self.assertTrue(not t1 == t2)
1672 self.assertTrue(not t2 == t1)
1673 self.assertTrue(not t1 > t2)
1674 self.assertTrue(not t2 < t1)
1675 self.assertTrue(not t1 >= t2)
1676 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001677
1678
1679 # A helper for timestamp constructor tests.
1680 def verify_field_equality(self, expected, got):
1681 self.assertEqual(expected.tm_year, got.year)
1682 self.assertEqual(expected.tm_mon, got.month)
1683 self.assertEqual(expected.tm_mday, got.day)
1684 self.assertEqual(expected.tm_hour, got.hour)
1685 self.assertEqual(expected.tm_min, got.minute)
1686 self.assertEqual(expected.tm_sec, got.second)
1687
1688 def test_fromtimestamp(self):
1689 import time
1690
1691 ts = time.time()
1692 expected = time.localtime(ts)
1693 got = self.theclass.fromtimestamp(ts)
1694 self.verify_field_equality(expected, got)
1695
1696 def test_utcfromtimestamp(self):
1697 import time
1698
1699 ts = time.time()
1700 expected = time.gmtime(ts)
1701 got = self.theclass.utcfromtimestamp(ts)
1702 self.verify_field_equality(expected, got)
1703
Thomas Wouters477c8d52006-05-27 19:21:47 +00001704 def test_microsecond_rounding(self):
1705 # Test whether fromtimestamp "rounds up" floats that are less
1706 # than one microsecond smaller than an integer.
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001707 self.assertEqual(self.theclass.fromtimestamp(0.9999999),
1708 self.theclass.fromtimestamp(1))
Thomas Wouters477c8d52006-05-27 19:21:47 +00001709
Tim Peters1b6f7a92004-06-20 02:50:16 +00001710 def test_insane_fromtimestamp(self):
1711 # It's possible that some platform maps time_t to double,
1712 # and that this test will fail there. This test should
1713 # exempt such platforms (provided they return reasonable
1714 # results!).
1715 for insane in -1e200, 1e200:
1716 self.assertRaises(ValueError, self.theclass.fromtimestamp,
1717 insane)
1718
1719 def test_insane_utcfromtimestamp(self):
1720 # It's possible that some platform maps time_t to double,
1721 # and that this test will fail there. This test should
1722 # exempt such platforms (provided they return reasonable
1723 # results!).
1724 for insane in -1e200, 1e200:
1725 self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
1726 insane)
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001727 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001728 def test_negative_float_fromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001729 # The result is tz-dependent; at least test that this doesn't
1730 # fail (like it did before bug 1646728 was fixed).
1731 self.theclass.fromtimestamp(-1.05)
1732
Alexander Belopolskyc4e4a8d2010-05-26 20:48:30 +00001733 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
Guido van Rossumd8faa362007-04-27 19:54:29 +00001734 def test_negative_float_utcfromtimestamp(self):
Guido van Rossumd8faa362007-04-27 19:54:29 +00001735 d = self.theclass.utcfromtimestamp(-1.05)
Alexander Belopolsky05cc2032010-06-15 18:40:23 +00001736 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001737
Tim Peters2a799bf2002-12-16 20:18:38 +00001738 def test_utcnow(self):
1739 import time
1740
1741 # Call it a success if utcnow() and utcfromtimestamp() are within
1742 # a second of each other.
1743 tolerance = timedelta(seconds=1)
1744 for dummy in range(3):
1745 from_now = self.theclass.utcnow()
1746 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1747 if abs(from_timestamp - from_now) <= tolerance:
1748 break
1749 # Else try again a few times.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001750 self.assertTrue(abs(from_timestamp - from_now) <= tolerance)
Tim Peters2a799bf2002-12-16 20:18:38 +00001751
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001752 def test_strptime(self):
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001753 import _strptime
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001754
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001755 string = '2004-12-01 13:02:47.197'
1756 format = '%Y-%m-%d %H:%M:%S.%f'
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001757 expected = _strptime._strptime_datetime(self.theclass, string, format)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001758 got = self.theclass.strptime(string, format)
1759 self.assertEqual(expected, got)
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001760 self.assertIs(type(expected), self.theclass)
1761 self.assertIs(type(got), self.theclass)
Skip Montanaro0af3ade2005-01-13 04:12:31 +00001762
Alexander Belopolskyca94f552010-06-17 18:30:34 +00001763 strptime = self.theclass.strptime
1764 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1765 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1766 # Only local timezone and UTC are supported
1767 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1768 (-_time.timezone, _time.tzname[0])):
1769 if tzseconds < 0:
1770 sign = '-'
1771 seconds = -tzseconds
1772 else:
1773 sign ='+'
1774 seconds = tzseconds
1775 hours, minutes = divmod(seconds//60, 60)
1776 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1777 dt = strptime(dtstr, "%z %Z")
1778 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1779 self.assertEqual(dt.tzname(), tzname)
1780 # Can produce inconsistent datetime
1781 dtstr, fmt = "+1234 UTC", "%z %Z"
1782 dt = strptime(dtstr, fmt)
1783 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1784 self.assertEqual(dt.tzname(), 'UTC')
1785 # yet will roundtrip
1786 self.assertEqual(dt.strftime(fmt), dtstr)
1787
1788 # Produce naive datetime if no %z is provided
1789 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1790
1791 with self.assertRaises(ValueError): strptime("-2400", "%z")
1792 with self.assertRaises(ValueError): strptime("-000", "%z")
1793
Tim Peters2a799bf2002-12-16 20:18:38 +00001794 def test_more_timetuple(self):
1795 # This tests fields beyond those tested by the TestDate.test_timetuple.
1796 t = self.theclass(2004, 12, 31, 6, 22, 33)
1797 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1798 self.assertEqual(t.timetuple(),
1799 (t.year, t.month, t.day,
1800 t.hour, t.minute, t.second,
1801 t.weekday(),
1802 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1803 -1))
1804 tt = t.timetuple()
1805 self.assertEqual(tt.tm_year, t.year)
1806 self.assertEqual(tt.tm_mon, t.month)
1807 self.assertEqual(tt.tm_mday, t.day)
1808 self.assertEqual(tt.tm_hour, t.hour)
1809 self.assertEqual(tt.tm_min, t.minute)
1810 self.assertEqual(tt.tm_sec, t.second)
1811 self.assertEqual(tt.tm_wday, t.weekday())
1812 self.assertEqual(tt.tm_yday, t.toordinal() -
1813 date(t.year, 1, 1).toordinal() + 1)
1814 self.assertEqual(tt.tm_isdst, -1)
1815
1816 def test_more_strftime(self):
1817 # This tests fields beyond those tested by the TestDate.test_strftime.
Christian Heimesdd15f6c2008-03-16 00:07:10 +00001818 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1819 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1820 "12 31 04 000047 33 22 06 366")
Tim Peters2a799bf2002-12-16 20:18:38 +00001821
1822 def test_extract(self):
1823 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1824 self.assertEqual(dt.date(), date(2002, 3, 4))
1825 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1826
1827 def test_combine(self):
1828 d = date(2002, 3, 4)
1829 t = time(18, 45, 3, 1234)
1830 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1831 combine = self.theclass.combine
1832 dt = combine(d, t)
1833 self.assertEqual(dt, expected)
1834
1835 dt = combine(time=t, date=d)
1836 self.assertEqual(dt, expected)
1837
1838 self.assertEqual(d, dt.date())
1839 self.assertEqual(t, dt.time())
1840 self.assertEqual(dt, combine(dt.date(), dt.time()))
1841
1842 self.assertRaises(TypeError, combine) # need an arg
1843 self.assertRaises(TypeError, combine, d) # need two args
1844 self.assertRaises(TypeError, combine, t, d) # args reversed
1845 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1846 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1847
Tim Peters12bf3392002-12-24 05:41:27 +00001848 def test_replace(self):
1849 cls = self.theclass
1850 args = [1, 2, 3, 4, 5, 6, 7]
1851 base = cls(*args)
1852 self.assertEqual(base, base.replace())
1853
1854 i = 0
1855 for name, newval in (("year", 2),
1856 ("month", 3),
1857 ("day", 4),
1858 ("hour", 5),
1859 ("minute", 6),
1860 ("second", 7),
1861 ("microsecond", 8)):
1862 newargs = args[:]
1863 newargs[i] = newval
1864 expected = cls(*newargs)
1865 got = base.replace(**{name: newval})
1866 self.assertEqual(expected, got)
1867 i += 1
1868
1869 # Out of bounds.
1870 base = cls(2000, 2, 29)
1871 self.assertRaises(ValueError, base.replace, year=2001)
1872
Tim Peters80475bb2002-12-25 07:40:55 +00001873 def test_astimezone(self):
Tim Peters52dcce22003-01-23 16:36:11 +00001874 # Pretty boring! The TZ test is more interesting here. astimezone()
1875 # simply can't be applied to a naive object.
Tim Peters80475bb2002-12-25 07:40:55 +00001876 dt = self.theclass.now()
1877 f = FixedOffset(44, "")
Tim Peters80475bb2002-12-25 07:40:55 +00001878 self.assertRaises(TypeError, dt.astimezone) # not enough args
1879 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1880 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
Tim Peters52dcce22003-01-23 16:36:11 +00001881 self.assertRaises(ValueError, dt.astimezone, f) # naive
1882 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
Tim Peters80475bb2002-12-25 07:40:55 +00001883
Tim Peters52dcce22003-01-23 16:36:11 +00001884 class Bogus(tzinfo):
1885 def utcoffset(self, dt): return None
1886 def dst(self, dt): return timedelta(0)
1887 bog = Bogus()
1888 self.assertRaises(ValueError, dt.astimezone, bog) # naive
1889
1890 class AlsoBogus(tzinfo):
1891 def utcoffset(self, dt): return timedelta(0)
1892 def dst(self, dt): return None
1893 alsobog = AlsoBogus()
1894 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
Tim Peters12bf3392002-12-24 05:41:27 +00001895
Tim Petersa98924a2003-05-17 05:55:19 +00001896 def test_subclass_datetime(self):
1897
1898 class C(self.theclass):
1899 theAnswer = 42
1900
1901 def __new__(cls, *args, **kws):
1902 temp = kws.copy()
1903 extra = temp.pop('extra')
1904 result = self.theclass.__new__(cls, *args, **temp)
1905 result.extra = extra
1906 return result
1907
1908 def newmeth(self, start):
1909 return start + self.year + self.month + self.second
1910
1911 args = 2003, 4, 14, 12, 13, 41
1912
1913 dt1 = self.theclass(*args)
1914 dt2 = C(*args, **{'extra': 7})
1915
1916 self.assertEqual(dt2.__class__, C)
1917 self.assertEqual(dt2.theAnswer, 42)
1918 self.assertEqual(dt2.extra, 7)
1919 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1920 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
1921 dt1.second - 7)
1922
Alexander Belopolskyf5682182010-06-18 18:44:37 +00001923class TestSubclassDateTime(TestDateTime):
1924 theclass = SubclassDatetime
1925 # Override tests not designed for subclass
1926 def test_roundtrip(self):
1927 pass
1928
Tim Peters604c0132004-06-07 23:04:33 +00001929class SubclassTime(time):
1930 sub_var = 1
1931
Guido van Rossumd8faa362007-04-27 19:54:29 +00001932class TestTime(HarmlessMixedComparison, unittest.TestCase):
Tim Peters2a799bf2002-12-16 20:18:38 +00001933
1934 theclass = time
1935
1936 def test_basic_attributes(self):
1937 t = self.theclass(12, 0)
1938 self.assertEqual(t.hour, 12)
1939 self.assertEqual(t.minute, 0)
1940 self.assertEqual(t.second, 0)
1941 self.assertEqual(t.microsecond, 0)
1942
1943 def test_basic_attributes_nonzero(self):
1944 # Make sure all attributes are non-zero so bugs in
1945 # bit-shifting access show up.
1946 t = self.theclass(12, 59, 59, 8000)
1947 self.assertEqual(t.hour, 12)
1948 self.assertEqual(t.minute, 59)
1949 self.assertEqual(t.second, 59)
1950 self.assertEqual(t.microsecond, 8000)
1951
1952 def test_roundtrip(self):
1953 t = self.theclass(1, 2, 3, 4)
1954
1955 # Verify t -> string -> time identity.
1956 s = repr(t)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001957 self.assertTrue(s.startswith('datetime.'))
Tim Peters2a799bf2002-12-16 20:18:38 +00001958 s = s[9:]
1959 t2 = eval(s)
1960 self.assertEqual(t, t2)
1961
1962 # Verify identity via reconstructing from pieces.
1963 t2 = self.theclass(t.hour, t.minute, t.second,
1964 t.microsecond)
1965 self.assertEqual(t, t2)
1966
1967 def test_comparing(self):
1968 args = [1, 2, 3, 4]
1969 t1 = self.theclass(*args)
1970 t2 = self.theclass(*args)
Guido van Rossume61fd5b2007-07-11 12:20:59 +00001971 self.assertEqual(t1, t2)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001972 self.assertTrue(t1 <= t2)
1973 self.assertTrue(t1 >= t2)
1974 self.assertTrue(not t1 != t2)
1975 self.assertTrue(not t1 < t2)
1976 self.assertTrue(not t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00001977
1978 for i in range(len(args)):
1979 newargs = args[:]
1980 newargs[i] = args[i] + 1
1981 t2 = self.theclass(*newargs) # this is larger than t1
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001982 self.assertTrue(t1 < t2)
1983 self.assertTrue(t2 > t1)
1984 self.assertTrue(t1 <= t2)
1985 self.assertTrue(t2 >= t1)
1986 self.assertTrue(t1 != t2)
1987 self.assertTrue(t2 != t1)
1988 self.assertTrue(not t1 == t2)
1989 self.assertTrue(not t2 == t1)
1990 self.assertTrue(not t1 > t2)
1991 self.assertTrue(not t2 < t1)
1992 self.assertTrue(not t1 >= t2)
1993 self.assertTrue(not t2 <= t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00001994
Tim Peters68124bb2003-02-08 03:46:31 +00001995 for badarg in OTHERSTUFF:
Tim Peters07534a62003-02-07 22:50:28 +00001996 self.assertEqual(t1 == badarg, False)
1997 self.assertEqual(t1 != badarg, True)
1998 self.assertEqual(badarg == t1, False)
1999 self.assertEqual(badarg != t1, True)
2000
Tim Peters2a799bf2002-12-16 20:18:38 +00002001 self.assertRaises(TypeError, lambda: t1 <= badarg)
2002 self.assertRaises(TypeError, lambda: t1 < badarg)
2003 self.assertRaises(TypeError, lambda: t1 > badarg)
2004 self.assertRaises(TypeError, lambda: t1 >= badarg)
Tim Peters2a799bf2002-12-16 20:18:38 +00002005 self.assertRaises(TypeError, lambda: badarg <= t1)
2006 self.assertRaises(TypeError, lambda: badarg < t1)
2007 self.assertRaises(TypeError, lambda: badarg > t1)
2008 self.assertRaises(TypeError, lambda: badarg >= t1)
2009
2010 def test_bad_constructor_arguments(self):
2011 # bad hours
2012 self.theclass(0, 0) # no exception
2013 self.theclass(23, 0) # no exception
2014 self.assertRaises(ValueError, self.theclass, -1, 0)
2015 self.assertRaises(ValueError, self.theclass, 24, 0)
2016 # bad minutes
2017 self.theclass(23, 0) # no exception
2018 self.theclass(23, 59) # no exception
2019 self.assertRaises(ValueError, self.theclass, 23, -1)
2020 self.assertRaises(ValueError, self.theclass, 23, 60)
2021 # bad seconds
2022 self.theclass(23, 59, 0) # no exception
2023 self.theclass(23, 59, 59) # no exception
2024 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2025 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2026 # bad microseconds
2027 self.theclass(23, 59, 59, 0) # no exception
2028 self.theclass(23, 59, 59, 999999) # no exception
2029 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2030 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2031
2032 def test_hash_equality(self):
2033 d = self.theclass(23, 30, 17)
2034 e = self.theclass(23, 30, 17)
2035 self.assertEqual(d, e)
2036 self.assertEqual(hash(d), hash(e))
2037
2038 dic = {d: 1}
2039 dic[e] = 2
2040 self.assertEqual(len(dic), 1)
2041 self.assertEqual(dic[d], 2)
2042 self.assertEqual(dic[e], 2)
2043
2044 d = self.theclass(0, 5, 17)
2045 e = self.theclass(0, 5, 17)
2046 self.assertEqual(d, e)
2047 self.assertEqual(hash(d), hash(e))
2048
2049 dic = {d: 1}
2050 dic[e] = 2
2051 self.assertEqual(len(dic), 1)
2052 self.assertEqual(dic[d], 2)
2053 self.assertEqual(dic[e], 2)
2054
2055 def test_isoformat(self):
2056 t = self.theclass(4, 5, 1, 123)
2057 self.assertEqual(t.isoformat(), "04:05:01.000123")
2058 self.assertEqual(t.isoformat(), str(t))
2059
2060 t = self.theclass()
2061 self.assertEqual(t.isoformat(), "00:00:00")
2062 self.assertEqual(t.isoformat(), str(t))
2063
2064 t = self.theclass(microsecond=1)
2065 self.assertEqual(t.isoformat(), "00:00:00.000001")
2066 self.assertEqual(t.isoformat(), str(t))
2067
2068 t = self.theclass(microsecond=10)
2069 self.assertEqual(t.isoformat(), "00:00:00.000010")
2070 self.assertEqual(t.isoformat(), str(t))
2071
2072 t = self.theclass(microsecond=100)
2073 self.assertEqual(t.isoformat(), "00:00:00.000100")
2074 self.assertEqual(t.isoformat(), str(t))
2075
2076 t = self.theclass(microsecond=1000)
2077 self.assertEqual(t.isoformat(), "00:00:00.001000")
2078 self.assertEqual(t.isoformat(), str(t))
2079
2080 t = self.theclass(microsecond=10000)
2081 self.assertEqual(t.isoformat(), "00:00:00.010000")
2082 self.assertEqual(t.isoformat(), str(t))
2083
2084 t = self.theclass(microsecond=100000)
2085 self.assertEqual(t.isoformat(), "00:00:00.100000")
2086 self.assertEqual(t.isoformat(), str(t))
2087
Thomas Wouterscf297e42007-02-23 15:07:44 +00002088 def test_1653736(self):
2089 # verify it doesn't accept extra keyword arguments
2090 t = self.theclass(second=1)
2091 self.assertRaises(TypeError, t.isoformat, foo=3)
2092
Tim Peters2a799bf2002-12-16 20:18:38 +00002093 def test_strftime(self):
2094 t = self.theclass(1, 2, 3, 4)
Christian Heimesdd15f6c2008-03-16 00:07:10 +00002095 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
Tim Peters2a799bf2002-12-16 20:18:38 +00002096 # A naive object replaces %z and %Z with empty strings.
2097 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2098
Eric Smith1ba31142007-09-11 18:06:02 +00002099 def test_format(self):
2100 t = self.theclass(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002101 self.assertEqual(t.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002102
2103 # check that a derived class's __str__() gets called
2104 class A(self.theclass):
2105 def __str__(self):
2106 return 'A'
2107 a = A(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002108 self.assertEqual(a.__format__(''), 'A')
Eric Smith1ba31142007-09-11 18:06:02 +00002109
2110 # check that a derived class's strftime gets called
2111 class B(self.theclass):
2112 def strftime(self, format_spec):
2113 return 'B'
2114 b = B(1, 2, 3, 4)
Eric Smith8fd3eba2008-02-17 19:48:00 +00002115 self.assertEqual(b.__format__(''), str(t))
Eric Smith1ba31142007-09-11 18:06:02 +00002116
2117 for fmt in ['%H %M %S',
2118 ]:
Eric Smith8fd3eba2008-02-17 19:48:00 +00002119 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2120 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2121 self.assertEqual(b.__format__(fmt), 'B')
Eric Smith1ba31142007-09-11 18:06:02 +00002122
Tim Peters2a799bf2002-12-16 20:18:38 +00002123 def test_str(self):
2124 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2125 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2126 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2127 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2128 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2129
2130 def test_repr(self):
2131 name = 'datetime.' + self.theclass.__name__
2132 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2133 "%s(1, 2, 3, 4)" % name)
2134 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2135 "%s(10, 2, 3, 4000)" % name)
2136 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2137 "%s(0, 2, 3, 400000)" % name)
2138 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2139 "%s(12, 2, 3)" % name)
2140 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2141 "%s(23, 15)" % name)
2142
2143 def test_resolution_info(self):
Ezio Melottie9615932010-01-24 19:26:24 +00002144 self.assertIsInstance(self.theclass.min, self.theclass)
2145 self.assertIsInstance(self.theclass.max, self.theclass)
2146 self.assertIsInstance(self.theclass.resolution, timedelta)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002147 self.assertTrue(self.theclass.max > self.theclass.min)
Tim Peters2a799bf2002-12-16 20:18:38 +00002148
2149 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002150 args = 20, 59, 16, 64**2
2151 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002152 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002153 green = pickler.dumps(orig, proto)
2154 derived = unpickler.loads(green)
2155 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002156
Tim Peters604c0132004-06-07 23:04:33 +00002157 def test_pickling_subclass_time(self):
2158 args = 20, 59, 16, 64**2
2159 orig = SubclassTime(*args)
2160 for pickler, unpickler, proto in pickle_choices:
2161 green = pickler.dumps(orig, proto)
2162 derived = unpickler.loads(green)
2163 self.assertEqual(orig, derived)
2164
Tim Peters2a799bf2002-12-16 20:18:38 +00002165 def test_bool(self):
2166 cls = self.theclass
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002167 self.assertTrue(cls(1))
2168 self.assertTrue(cls(0, 1))
2169 self.assertTrue(cls(0, 0, 1))
2170 self.assertTrue(cls(0, 0, 0, 1))
2171 self.assertTrue(not cls(0))
2172 self.assertTrue(not cls())
Tim Peters2a799bf2002-12-16 20:18:38 +00002173
Tim Peters12bf3392002-12-24 05:41:27 +00002174 def test_replace(self):
2175 cls = self.theclass
2176 args = [1, 2, 3, 4]
2177 base = cls(*args)
2178 self.assertEqual(base, base.replace())
2179
2180 i = 0
2181 for name, newval in (("hour", 5),
2182 ("minute", 6),
2183 ("second", 7),
2184 ("microsecond", 8)):
2185 newargs = args[:]
2186 newargs[i] = newval
2187 expected = cls(*newargs)
2188 got = base.replace(**{name: newval})
2189 self.assertEqual(expected, got)
2190 i += 1
2191
2192 # Out of bounds.
2193 base = cls(1)
2194 self.assertRaises(ValueError, base.replace, hour=24)
2195 self.assertRaises(ValueError, base.replace, minute=-1)
2196 self.assertRaises(ValueError, base.replace, second=100)
2197 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2198
Tim Petersa98924a2003-05-17 05:55:19 +00002199 def test_subclass_time(self):
2200
2201 class C(self.theclass):
2202 theAnswer = 42
2203
2204 def __new__(cls, *args, **kws):
2205 temp = kws.copy()
2206 extra = temp.pop('extra')
2207 result = self.theclass.__new__(cls, *args, **temp)
2208 result.extra = extra
2209 return result
2210
2211 def newmeth(self, start):
2212 return start + self.hour + self.second
2213
2214 args = 4, 5, 6
2215
2216 dt1 = self.theclass(*args)
2217 dt2 = C(*args, **{'extra': 7})
2218
2219 self.assertEqual(dt2.__class__, C)
2220 self.assertEqual(dt2.theAnswer, 42)
2221 self.assertEqual(dt2.extra, 7)
2222 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2223 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2224
Armin Rigof4afb212005-11-07 07:15:48 +00002225 def test_backdoor_resistance(self):
2226 # see TestDate.test_backdoor_resistance().
2227 base = '2:59.0'
2228 for hour_byte in ' ', '9', chr(24), '\xff':
2229 self.assertRaises(TypeError, self.theclass,
2230 hour_byte + base[1:])
2231
Tim Peters855fe882002-12-22 03:43:39 +00002232# A mixin for classes with a tzinfo= argument. Subclasses must define
2233# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002234# must be legit (which is true for time and datetime).
Guido van Rossumd8faa362007-04-27 19:54:29 +00002235class TZInfoBase:
Tim Peters2a799bf2002-12-16 20:18:38 +00002236
Tim Petersbad8ff02002-12-30 20:52:32 +00002237 def test_argument_passing(self):
2238 cls = self.theclass
Tim Peters0bf60bd2003-01-08 20:40:01 +00002239 # A datetime passes itself on, a time passes None.
Tim Petersbad8ff02002-12-30 20:52:32 +00002240 class introspective(tzinfo):
2241 def tzname(self, dt): return dt and "real" or "none"
Tim Peters397301e2003-01-02 21:28:08 +00002242 def utcoffset(self, dt):
2243 return timedelta(minutes = dt and 42 or -42)
Tim Petersbad8ff02002-12-30 20:52:32 +00002244 dst = utcoffset
2245
2246 obj = cls(1, 2, 3, tzinfo=introspective())
2247
Tim Peters0bf60bd2003-01-08 20:40:01 +00002248 expected = cls is time and "none" or "real"
Tim Petersbad8ff02002-12-30 20:52:32 +00002249 self.assertEqual(obj.tzname(), expected)
2250
Tim Peters0bf60bd2003-01-08 20:40:01 +00002251 expected = timedelta(minutes=(cls is time and -42 or 42))
Tim Petersbad8ff02002-12-30 20:52:32 +00002252 self.assertEqual(obj.utcoffset(), expected)
2253 self.assertEqual(obj.dst(), expected)
2254
Tim Peters855fe882002-12-22 03:43:39 +00002255 def test_bad_tzinfo_classes(self):
2256 cls = self.theclass
2257 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
Tim Peters2a799bf2002-12-16 20:18:38 +00002258
Tim Peters855fe882002-12-22 03:43:39 +00002259 class NiceTry(object):
2260 def __init__(self): pass
2261 def utcoffset(self, dt): pass
2262 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2263
2264 class BetterTry(tzinfo):
2265 def __init__(self): pass
2266 def utcoffset(self, dt): pass
2267 b = BetterTry()
2268 t = cls(1, 1, 1, tzinfo=b)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002269 self.assertTrue(t.tzinfo is b)
Tim Peters855fe882002-12-22 03:43:39 +00002270
2271 def test_utc_offset_out_of_bounds(self):
2272 class Edgy(tzinfo):
2273 def __init__(self, offset):
Tim Peters397301e2003-01-02 21:28:08 +00002274 self.offset = timedelta(minutes=offset)
Tim Peters855fe882002-12-22 03:43:39 +00002275 def utcoffset(self, dt):
2276 return self.offset
2277
2278 cls = self.theclass
2279 for offset, legit in ((-1440, False),
2280 (-1439, True),
2281 (1439, True),
2282 (1440, False)):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002283 if cls is time:
Tim Peters855fe882002-12-22 03:43:39 +00002284 t = cls(1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002285 elif cls is datetime:
Tim Peters855fe882002-12-22 03:43:39 +00002286 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002287 else:
2288 assert 0, "impossible"
Tim Peters855fe882002-12-22 03:43:39 +00002289 if legit:
2290 aofs = abs(offset)
2291 h, m = divmod(aofs, 60)
2292 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002293 if isinstance(t, datetime):
Tim Peters855fe882002-12-22 03:43:39 +00002294 t = t.timetz()
2295 self.assertEqual(str(t), "01:02:03" + tag)
2296 else:
2297 self.assertRaises(ValueError, str, t)
2298
2299 def test_tzinfo_classes(self):
2300 cls = self.theclass
2301 class C1(tzinfo):
2302 def utcoffset(self, dt): return None
2303 def dst(self, dt): return None
2304 def tzname(self, dt): return None
2305 for t in (cls(1, 1, 1),
2306 cls(1, 1, 1, tzinfo=None),
2307 cls(1, 1, 1, tzinfo=C1())):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002308 self.assertTrue(t.utcoffset() is None)
2309 self.assertTrue(t.dst() is None)
2310 self.assertTrue(t.tzname() is None)
Tim Peters855fe882002-12-22 03:43:39 +00002311
Tim Peters855fe882002-12-22 03:43:39 +00002312 class C3(tzinfo):
2313 def utcoffset(self, dt): return timedelta(minutes=-1439)
2314 def dst(self, dt): return timedelta(minutes=1439)
2315 def tzname(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002316 t = cls(1, 1, 1, tzinfo=C3())
2317 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2318 self.assertEqual(t.dst(), timedelta(minutes=1439))
2319 self.assertEqual(t.tzname(), "aname")
Tim Peters855fe882002-12-22 03:43:39 +00002320
2321 # Wrong types.
2322 class C4(tzinfo):
2323 def utcoffset(self, dt): return "aname"
Tim Peters397301e2003-01-02 21:28:08 +00002324 def dst(self, dt): return 7
Tim Peters855fe882002-12-22 03:43:39 +00002325 def tzname(self, dt): return 0
2326 t = cls(1, 1, 1, tzinfo=C4())
2327 self.assertRaises(TypeError, t.utcoffset)
2328 self.assertRaises(TypeError, t.dst)
2329 self.assertRaises(TypeError, t.tzname)
2330
2331 # Offset out of range.
Tim Peters855fe882002-12-22 03:43:39 +00002332 class C6(tzinfo):
2333 def utcoffset(self, dt): return timedelta(hours=-24)
2334 def dst(self, dt): return timedelta(hours=24)
Tim Peters397301e2003-01-02 21:28:08 +00002335 t = cls(1, 1, 1, tzinfo=C6())
2336 self.assertRaises(ValueError, t.utcoffset)
2337 self.assertRaises(ValueError, t.dst)
Tim Peters855fe882002-12-22 03:43:39 +00002338
2339 # Not a whole number of minutes.
2340 class C7(tzinfo):
2341 def utcoffset(self, dt): return timedelta(seconds=61)
2342 def dst(self, dt): return timedelta(microseconds=-81)
2343 t = cls(1, 1, 1, tzinfo=C7())
2344 self.assertRaises(ValueError, t.utcoffset)
2345 self.assertRaises(ValueError, t.dst)
2346
Tim Peters4c0db782002-12-26 05:01:19 +00002347 def test_aware_compare(self):
2348 cls = self.theclass
2349
Tim Peters60c76e42002-12-27 00:41:11 +00002350 # Ensure that utcoffset() gets ignored if the comparands have
2351 # the same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00002352 class OperandDependentOffset(tzinfo):
2353 def utcoffset(self, t):
2354 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00002355 # d0 and d1 equal after adjustment
2356 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00002357 else:
Tim Peters397301e2003-01-02 21:28:08 +00002358 # d2 off in the weeds
2359 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00002360
2361 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2362 d0 = base.replace(minute=3)
2363 d1 = base.replace(minute=9)
2364 d2 = base.replace(minute=11)
2365 for x in d0, d1, d2:
2366 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002367 for op in lt, le, gt, ge, eq, ne:
2368 got = op(x, y)
2369 expected = op(x.minute, y.minute)
2370 self.assertEqual(got, expected)
Tim Peters60c76e42002-12-27 00:41:11 +00002371
2372 # However, if they're different members, uctoffset is not ignored.
Tim Peters0bf60bd2003-01-08 20:40:01 +00002373 # Note that a time can't actually have an operand-depedent offset,
2374 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2375 # so skip this test for time.
2376 if cls is not time:
Tim Petersbad8ff02002-12-30 20:52:32 +00002377 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2378 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2379 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2380 for x in d0, d1, d2:
2381 for y in d0, d1, d2:
Mark Dickinsona56c4672009-01-27 18:17:45 +00002382 got = (x > y) - (x < y)
Tim Petersbad8ff02002-12-30 20:52:32 +00002383 if (x is d0 or x is d1) and (y is d0 or y is d1):
2384 expected = 0
2385 elif x is y is d2:
2386 expected = 0
2387 elif x is d2:
2388 expected = -1
2389 else:
2390 assert y is d2
2391 expected = 1
2392 self.assertEqual(got, expected)
Tim Peters4c0db782002-12-26 05:01:19 +00002393
Tim Peters855fe882002-12-22 03:43:39 +00002394
Tim Peters0bf60bd2003-01-08 20:40:01 +00002395# Testing time objects with a non-None tzinfo.
Guido van Rossumd8faa362007-04-27 19:54:29 +00002396class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002397 theclass = time
Tim Peters2a799bf2002-12-16 20:18:38 +00002398
2399 def test_empty(self):
2400 t = self.theclass()
2401 self.assertEqual(t.hour, 0)
2402 self.assertEqual(t.minute, 0)
2403 self.assertEqual(t.second, 0)
2404 self.assertEqual(t.microsecond, 0)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002405 self.assertTrue(t.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002406
Tim Peters2a799bf2002-12-16 20:18:38 +00002407 def test_zones(self):
2408 est = FixedOffset(-300, "EST", 1)
2409 utc = FixedOffset(0, "UTC", -2)
2410 met = FixedOffset(60, "MET", 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002411 t1 = time( 7, 47, tzinfo=est)
2412 t2 = time(12, 47, tzinfo=utc)
2413 t3 = time(13, 47, tzinfo=met)
2414 t4 = time(microsecond=40)
2415 t5 = time(microsecond=40, tzinfo=utc)
Tim Peters2a799bf2002-12-16 20:18:38 +00002416
2417 self.assertEqual(t1.tzinfo, est)
2418 self.assertEqual(t2.tzinfo, utc)
2419 self.assertEqual(t3.tzinfo, met)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002420 self.assertTrue(t4.tzinfo is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002421 self.assertEqual(t5.tzinfo, utc)
2422
Tim Peters855fe882002-12-22 03:43:39 +00002423 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2424 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2425 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002426 self.assertTrue(t4.utcoffset() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002427 self.assertRaises(TypeError, t1.utcoffset, "no args")
2428
2429 self.assertEqual(t1.tzname(), "EST")
2430 self.assertEqual(t2.tzname(), "UTC")
2431 self.assertEqual(t3.tzname(), "MET")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002432 self.assertTrue(t4.tzname() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002433 self.assertRaises(TypeError, t1.tzname, "no args")
2434
Tim Peters855fe882002-12-22 03:43:39 +00002435 self.assertEqual(t1.dst(), timedelta(minutes=1))
2436 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2437 self.assertEqual(t3.dst(), timedelta(minutes=3))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002438 self.assertTrue(t4.dst() is None)
Tim Peters2a799bf2002-12-16 20:18:38 +00002439 self.assertRaises(TypeError, t1.dst, "no args")
2440
2441 self.assertEqual(hash(t1), hash(t2))
2442 self.assertEqual(hash(t1), hash(t3))
2443 self.assertEqual(hash(t2), hash(t3))
2444
2445 self.assertEqual(t1, t2)
2446 self.assertEqual(t1, t3)
2447 self.assertEqual(t2, t3)
2448 self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
2449 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2450 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2451
2452 self.assertEqual(str(t1), "07:47:00-05:00")
2453 self.assertEqual(str(t2), "12:47:00+00:00")
2454 self.assertEqual(str(t3), "13:47:00+01:00")
2455 self.assertEqual(str(t4), "00:00:00.000040")
2456 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2457
2458 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2459 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2460 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2461 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2462 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2463
Tim Peters0bf60bd2003-01-08 20:40:01 +00002464 d = 'datetime.time'
Tim Peters2a799bf2002-12-16 20:18:38 +00002465 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2466 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2467 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2468 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2469 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2470
2471 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2472 "07:47:00 %Z=EST %z=-0500")
2473 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2474 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2475
2476 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002477 t1 = time(23, 59, tzinfo=yuck)
Tim Peters2a799bf2002-12-16 20:18:38 +00002478 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2479 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2480
Tim Petersb92bb712002-12-21 17:44:07 +00002481 # Check that an invalid tzname result raises an exception.
2482 class Badtzname(tzinfo):
2483 def tzname(self, dt): return 42
Tim Peters0bf60bd2003-01-08 20:40:01 +00002484 t = time(2, 3, 4, tzinfo=Badtzname())
Tim Petersb92bb712002-12-21 17:44:07 +00002485 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2486 self.assertRaises(TypeError, t.strftime, "%Z")
Tim Peters2a799bf2002-12-16 20:18:38 +00002487
2488 def test_hash_edge_cases(self):
2489 # Offsets that overflow a basic time.
2490 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2491 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2492 self.assertEqual(hash(t1), hash(t2))
2493
2494 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2495 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2496 self.assertEqual(hash(t1), hash(t2))
2497
Tim Peters2a799bf2002-12-16 20:18:38 +00002498 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002499 # Try one without a tzinfo.
2500 args = 20, 59, 16, 64**2
2501 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002502 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002503 green = pickler.dumps(orig, proto)
2504 derived = unpickler.loads(green)
2505 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002506
2507 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002508 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002509 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002510 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002511 green = pickler.dumps(orig, proto)
2512 derived = unpickler.loads(green)
2513 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002514 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002515 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2516 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002517
2518 def test_more_bool(self):
2519 # Test cases with non-None tzinfo.
2520 cls = self.theclass
2521
2522 t = cls(0, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002523 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002524
2525 t = cls(5, tzinfo=FixedOffset(-300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002526 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002527
2528 t = cls(5, tzinfo=FixedOffset(300, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002529 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002530
2531 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002532 self.assertTrue(not t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002533
2534 # Mostly ensuring this doesn't overflow internally.
2535 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002536 self.assertTrue(t)
Tim Peters2a799bf2002-12-16 20:18:38 +00002537
2538 # But this should yield a value error -- the utcoffset is bogus.
2539 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2540 self.assertRaises(ValueError, lambda: bool(t))
2541
2542 # Likewise.
2543 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2544 self.assertRaises(ValueError, lambda: bool(t))
2545
Tim Peters12bf3392002-12-24 05:41:27 +00002546 def test_replace(self):
2547 cls = self.theclass
2548 z100 = FixedOffset(100, "+100")
2549 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2550 args = [1, 2, 3, 4, z100]
2551 base = cls(*args)
2552 self.assertEqual(base, base.replace())
2553
2554 i = 0
2555 for name, newval in (("hour", 5),
2556 ("minute", 6),
2557 ("second", 7),
2558 ("microsecond", 8),
2559 ("tzinfo", zm200)):
2560 newargs = args[:]
2561 newargs[i] = newval
2562 expected = cls(*newargs)
2563 got = base.replace(**{name: newval})
2564 self.assertEqual(expected, got)
2565 i += 1
2566
2567 # Ensure we can get rid of a tzinfo.
2568 self.assertEqual(base.tzname(), "+100")
2569 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002570 self.assertTrue(base2.tzinfo is None)
2571 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00002572
2573 # Ensure we can add one.
2574 base3 = base2.replace(tzinfo=z100)
2575 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002576 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00002577
2578 # Out of bounds.
2579 base = cls(1)
2580 self.assertRaises(ValueError, base.replace, hour=24)
2581 self.assertRaises(ValueError, base.replace, minute=-1)
2582 self.assertRaises(ValueError, base.replace, second=100)
2583 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2584
Tim Peters60c76e42002-12-27 00:41:11 +00002585 def test_mixed_compare(self):
2586 t1 = time(1, 2, 3)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002587 t2 = time(1, 2, 3)
Tim Peters60c76e42002-12-27 00:41:11 +00002588 self.assertEqual(t1, t2)
2589 t2 = t2.replace(tzinfo=None)
2590 self.assertEqual(t1, t2)
2591 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2592 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00002593 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
2594 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00002595
Tim Peters0bf60bd2003-01-08 20:40:01 +00002596 # In time w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00002597 class Varies(tzinfo):
2598 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00002599 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00002600 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00002601 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00002602 return self.offset
2603
2604 v = Varies()
2605 t1 = t2.replace(tzinfo=v)
2606 t2 = t2.replace(tzinfo=v)
2607 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2608 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2609 self.assertEqual(t1, t2)
2610
2611 # But if they're not identical, it isn't ignored.
2612 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002613 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters60c76e42002-12-27 00:41:11 +00002614
Tim Petersa98924a2003-05-17 05:55:19 +00002615 def test_subclass_timetz(self):
2616
2617 class C(self.theclass):
2618 theAnswer = 42
2619
2620 def __new__(cls, *args, **kws):
2621 temp = kws.copy()
2622 extra = temp.pop('extra')
2623 result = self.theclass.__new__(cls, *args, **temp)
2624 result.extra = extra
2625 return result
2626
2627 def newmeth(self, start):
2628 return start + self.hour + self.second
2629
2630 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2631
2632 dt1 = self.theclass(*args)
2633 dt2 = C(*args, **{'extra': 7})
2634
2635 self.assertEqual(dt2.__class__, C)
2636 self.assertEqual(dt2.theAnswer, 42)
2637 self.assertEqual(dt2.extra, 7)
2638 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2639 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2640
Tim Peters4c0db782002-12-26 05:01:19 +00002641
Tim Peters0bf60bd2003-01-08 20:40:01 +00002642# Testing datetime objects with a non-None tzinfo.
2643
Guido van Rossumd8faa362007-04-27 19:54:29 +00002644class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002645 theclass = datetime
Tim Peters2a799bf2002-12-16 20:18:38 +00002646
2647 def test_trivial(self):
2648 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2649 self.assertEqual(dt.year, 1)
2650 self.assertEqual(dt.month, 2)
2651 self.assertEqual(dt.day, 3)
2652 self.assertEqual(dt.hour, 4)
2653 self.assertEqual(dt.minute, 5)
2654 self.assertEqual(dt.second, 6)
2655 self.assertEqual(dt.microsecond, 7)
2656 self.assertEqual(dt.tzinfo, None)
2657
2658 def test_even_more_compare(self):
2659 # The test_compare() and test_more_compare() inherited from TestDate
2660 # and TestDateTime covered non-tzinfo cases.
2661
2662 # Smallest possible after UTC adjustment.
2663 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2664 # Largest possible after UTC adjustment.
2665 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2666 tzinfo=FixedOffset(-1439, ""))
2667
2668 # Make sure those compare correctly, and w/o overflow.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002669 self.assertTrue(t1 < t2)
2670 self.assertTrue(t1 != t2)
2671 self.assertTrue(t2 > t1)
Tim Peters2a799bf2002-12-16 20:18:38 +00002672
Guido van Rossume61fd5b2007-07-11 12:20:59 +00002673 self.assertEqual(t1, t1)
2674 self.assertEqual(t2, t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002675
2676 # Equal afer adjustment.
2677 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2678 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2679 self.assertEqual(t1, t2)
2680
2681 # Change t1 not to subtract a minute, and t1 should be larger.
2682 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002683 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002684
2685 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2686 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002687 self.assertTrue(t1 < t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002688
2689 # Back to the original t1, but make seconds resolve it.
2690 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2691 second=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002692 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002693
2694 # Likewise, but make microseconds resolve it.
2695 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2696 microsecond=1)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002697 self.assertTrue(t1 > t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002698
2699 # Make t2 naive and it should fail.
2700 t2 = self.theclass.min
2701 self.assertRaises(TypeError, lambda: t1 == t2)
2702 self.assertEqual(t2, t2)
2703
2704 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2705 class Naive(tzinfo):
2706 def utcoffset(self, dt): return None
2707 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
2708 self.assertRaises(TypeError, lambda: t1 == t2)
2709 self.assertEqual(t2, t2)
2710
2711 # OTOH, it's OK to compare two of these mixing the two ways of being
2712 # naive.
2713 t1 = self.theclass(5, 6, 7)
2714 self.assertEqual(t1, t2)
2715
2716 # Try a bogus uctoffset.
2717 class Bogus(tzinfo):
Tim Peters397301e2003-01-02 21:28:08 +00002718 def utcoffset(self, dt):
2719 return timedelta(minutes=1440) # out of bounds
Tim Peters2a799bf2002-12-16 20:18:38 +00002720 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2721 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
Tim Peters60c76e42002-12-27 00:41:11 +00002722 self.assertRaises(ValueError, lambda: t1 == t2)
Tim Peters2a799bf2002-12-16 20:18:38 +00002723
Tim Peters2a799bf2002-12-16 20:18:38 +00002724 def test_pickling(self):
Tim Peters2a799bf2002-12-16 20:18:38 +00002725 # Try one without a tzinfo.
2726 args = 6, 7, 23, 20, 59, 1, 64**2
2727 orig = self.theclass(*args)
Guido van Rossum177e41a2003-01-30 22:06:23 +00002728 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002729 green = pickler.dumps(orig, proto)
2730 derived = unpickler.loads(green)
2731 self.assertEqual(orig, derived)
Tim Peters2a799bf2002-12-16 20:18:38 +00002732
2733 # Try one with a tzinfo.
Tim Petersfb8472c2002-12-21 05:04:42 +00002734 tinfo = PicklableFixedOffset(-300, 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002735 orig = self.theclass(*args, **{'tzinfo': tinfo})
Tim Petersa9bc1682003-01-11 03:39:11 +00002736 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
Guido van Rossum177e41a2003-01-30 22:06:23 +00002737 for pickler, unpickler, proto in pickle_choices:
Tim Peters96940c92003-01-31 21:55:33 +00002738 green = pickler.dumps(orig, proto)
2739 derived = unpickler.loads(green)
2740 self.assertEqual(orig, derived)
Ezio Melottie9615932010-01-24 19:26:24 +00002741 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
Tim Peters96940c92003-01-31 21:55:33 +00002742 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2743 self.assertEqual(derived.tzname(), 'cookie')
Tim Peters2a799bf2002-12-16 20:18:38 +00002744
2745 def test_extreme_hashes(self):
2746 # If an attempt is made to hash these via subtracting the offset
2747 # then hashing a datetime object, OverflowError results. The
2748 # Python implementation used to blow up here.
2749 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2750 hash(t)
2751 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2752 tzinfo=FixedOffset(-1439, ""))
2753 hash(t)
2754
2755 # OTOH, an OOB offset should blow up.
2756 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2757 self.assertRaises(ValueError, hash, t)
2758
2759 def test_zones(self):
2760 est = FixedOffset(-300, "EST")
2761 utc = FixedOffset(0, "UTC")
2762 met = FixedOffset(60, "MET")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002763 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2764 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2765 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
Tim Peters2a799bf2002-12-16 20:18:38 +00002766 self.assertEqual(t1.tzinfo, est)
2767 self.assertEqual(t2.tzinfo, utc)
2768 self.assertEqual(t3.tzinfo, met)
Tim Peters855fe882002-12-22 03:43:39 +00002769 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2770 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2771 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Tim Peters2a799bf2002-12-16 20:18:38 +00002772 self.assertEqual(t1.tzname(), "EST")
2773 self.assertEqual(t2.tzname(), "UTC")
2774 self.assertEqual(t3.tzname(), "MET")
2775 self.assertEqual(hash(t1), hash(t2))
2776 self.assertEqual(hash(t1), hash(t3))
2777 self.assertEqual(hash(t2), hash(t3))
2778 self.assertEqual(t1, t2)
2779 self.assertEqual(t1, t3)
2780 self.assertEqual(t2, t3)
2781 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2782 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2783 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002784 d = 'datetime.datetime(2002, 3, 19, '
Tim Peters2a799bf2002-12-16 20:18:38 +00002785 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2786 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2787 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2788
2789 def test_combine(self):
2790 met = FixedOffset(60, "MET")
2791 d = date(2002, 3, 4)
Tim Peters0bf60bd2003-01-08 20:40:01 +00002792 tz = time(18, 45, 3, 1234, tzinfo=met)
2793 dt = datetime.combine(d, tz)
2794 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
Tim Peters2a799bf2002-12-16 20:18:38 +00002795 tzinfo=met))
2796
2797 def test_extract(self):
2798 met = FixedOffset(60, "MET")
2799 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2800 self.assertEqual(dt.date(), date(2002, 3, 4))
2801 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
Tim Peters0bf60bd2003-01-08 20:40:01 +00002802 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
Tim Peters2a799bf2002-12-16 20:18:38 +00002803
2804 def test_tz_aware_arithmetic(self):
2805 import random
2806
2807 now = self.theclass.now()
2808 tz55 = FixedOffset(-330, "west 5:30")
Tim Peters0bf60bd2003-01-08 20:40:01 +00002809 timeaware = now.time().replace(tzinfo=tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002810 nowaware = self.theclass.combine(now.date(), timeaware)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002811 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002812 self.assertEqual(nowaware.timetz(), timeaware)
2813
2814 # Can't mix aware and non-aware.
2815 self.assertRaises(TypeError, lambda: now - nowaware)
2816 self.assertRaises(TypeError, lambda: nowaware - now)
2817
Tim Peters0bf60bd2003-01-08 20:40:01 +00002818 # And adding datetime's doesn't make sense, aware or not.
Tim Peters2a799bf2002-12-16 20:18:38 +00002819 self.assertRaises(TypeError, lambda: now + nowaware)
2820 self.assertRaises(TypeError, lambda: nowaware + now)
2821 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2822
2823 # Subtracting should yield 0.
2824 self.assertEqual(now - now, timedelta(0))
2825 self.assertEqual(nowaware - nowaware, timedelta(0))
2826
2827 # Adding a delta should preserve tzinfo.
2828 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2829 nowawareplus = nowaware + delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002830 self.assertTrue(nowaware.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002831 nowawareplus2 = delta + nowaware
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002832 self.assertTrue(nowawareplus2.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002833 self.assertEqual(nowawareplus, nowawareplus2)
2834
2835 # that - delta should be what we started with, and that - what we
2836 # started with should be delta.
2837 diff = nowawareplus - delta
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002838 self.assertTrue(diff.tzinfo is tz55)
Tim Peters2a799bf2002-12-16 20:18:38 +00002839 self.assertEqual(nowaware, diff)
2840 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2841 self.assertEqual(nowawareplus - nowaware, delta)
2842
2843 # Make up a random timezone.
2844 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
Tim Peters4c0db782002-12-26 05:01:19 +00002845 # Attach it to nowawareplus.
2846 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002847 self.assertTrue(nowawareplus.tzinfo is tzr)
Tim Peters2a799bf2002-12-16 20:18:38 +00002848 # Make sure the difference takes the timezone adjustments into account.
2849 got = nowaware - nowawareplus
2850 # Expected: (nowaware base - nowaware offset) -
2851 # (nowawareplus base - nowawareplus offset) =
2852 # (nowaware base - nowawareplus base) +
2853 # (nowawareplus offset - nowaware offset) =
2854 # -delta + nowawareplus offset - nowaware offset
Tim Peters855fe882002-12-22 03:43:39 +00002855 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
Tim Peters2a799bf2002-12-16 20:18:38 +00002856 self.assertEqual(got, expected)
2857
2858 # Try max possible difference.
2859 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
2860 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2861 tzinfo=FixedOffset(-1439, "max"))
2862 maxdiff = max - min
2863 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
2864 timedelta(minutes=2*1439))
2865
2866 def test_tzinfo_now(self):
2867 meth = self.theclass.now
2868 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2869 base = meth()
2870 # Try with and without naming the keyword.
2871 off42 = FixedOffset(42, "42")
2872 another = meth(off42)
Tim Peters10cadce2003-01-23 19:58:02 +00002873 again = meth(tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002874 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002875 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002876 # Bad argument with and w/o naming the keyword.
2877 self.assertRaises(TypeError, meth, 16)
2878 self.assertRaises(TypeError, meth, tzinfo=16)
2879 # Bad keyword name.
2880 self.assertRaises(TypeError, meth, tinfo=off42)
2881 # Too many args.
2882 self.assertRaises(TypeError, meth, off42, off42)
2883
Tim Peters10cadce2003-01-23 19:58:02 +00002884 # We don't know which time zone we're in, and don't have a tzinfo
2885 # class to represent it, so seeing whether a tz argument actually
2886 # does a conversion is tricky.
Tim Peters10cadce2003-01-23 19:58:02 +00002887 utc = FixedOffset(0, "utc", 0)
Alexander Belopolsky4e749a12010-06-14 14:15:50 +00002888 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
2889 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
2890 for dummy in range(3):
2891 now = datetime.now(weirdtz)
2892 self.assertTrue(now.tzinfo is weirdtz)
2893 utcnow = datetime.utcnow().replace(tzinfo=utc)
2894 now2 = utcnow.astimezone(weirdtz)
2895 if abs(now - now2) < timedelta(seconds=30):
2896 break
2897 # Else the code is broken, or more than 30 seconds passed between
2898 # calls; assuming the latter, just try again.
2899 else:
2900 # Three strikes and we're out.
2901 self.fail("utcnow(), now(tz), or astimezone() may be broken")
Tim Peters10cadce2003-01-23 19:58:02 +00002902
Tim Peters2a799bf2002-12-16 20:18:38 +00002903 def test_tzinfo_fromtimestamp(self):
2904 import time
2905 meth = self.theclass.fromtimestamp
2906 ts = time.time()
2907 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2908 base = meth(ts)
2909 # Try with and without naming the keyword.
2910 off42 = FixedOffset(42, "42")
2911 another = meth(ts, off42)
Tim Peters2a44a8d2003-01-23 20:53:10 +00002912 again = meth(ts, tz=off42)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002913 self.assertTrue(another.tzinfo is again.tzinfo)
Tim Peters855fe882002-12-22 03:43:39 +00002914 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
Tim Peters2a799bf2002-12-16 20:18:38 +00002915 # Bad argument with and w/o naming the keyword.
2916 self.assertRaises(TypeError, meth, ts, 16)
2917 self.assertRaises(TypeError, meth, ts, tzinfo=16)
2918 # Bad keyword name.
2919 self.assertRaises(TypeError, meth, ts, tinfo=off42)
2920 # Too many args.
2921 self.assertRaises(TypeError, meth, ts, off42, off42)
2922 # Too few args.
2923 self.assertRaises(TypeError, meth)
2924
Tim Peters2a44a8d2003-01-23 20:53:10 +00002925 # Try to make sure tz= actually does some conversion.
Tim Peters84407612003-02-06 16:42:14 +00002926 timestamp = 1000000000
2927 utcdatetime = datetime.utcfromtimestamp(timestamp)
2928 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
2929 # But on some flavor of Mac, it's nowhere near that. So we can't have
2930 # any idea here what time that actually is, we can only test that
2931 # relative changes match.
2932 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
2933 tz = FixedOffset(utcoffset, "tz", 0)
2934 expected = utcdatetime + utcoffset
2935 got = datetime.fromtimestamp(timestamp, tz)
2936 self.assertEqual(expected, got.replace(tzinfo=None))
Tim Peters2a44a8d2003-01-23 20:53:10 +00002937
Tim Peters2a799bf2002-12-16 20:18:38 +00002938 def test_tzinfo_utcnow(self):
2939 meth = self.theclass.utcnow
2940 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2941 base = meth()
2942 # Try with and without naming the keyword; for whatever reason,
2943 # utcnow() doesn't accept a tzinfo argument.
2944 off42 = FixedOffset(42, "42")
2945 self.assertRaises(TypeError, meth, off42)
2946 self.assertRaises(TypeError, meth, tzinfo=off42)
2947
2948 def test_tzinfo_utcfromtimestamp(self):
2949 import time
2950 meth = self.theclass.utcfromtimestamp
2951 ts = time.time()
2952 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
2953 base = meth(ts)
2954 # Try with and without naming the keyword; for whatever reason,
2955 # utcfromtimestamp() doesn't accept a tzinfo argument.
2956 off42 = FixedOffset(42, "42")
2957 self.assertRaises(TypeError, meth, ts, off42)
2958 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
2959
2960 def test_tzinfo_timetuple(self):
Tim Peters0bf60bd2003-01-08 20:40:01 +00002961 # TestDateTime tested most of this. datetime adds a twist to the
Tim Peters2a799bf2002-12-16 20:18:38 +00002962 # DST flag.
2963 class DST(tzinfo):
2964 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002965 if isinstance(dstvalue, int):
2966 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00002967 self.dstvalue = dstvalue
2968 def dst(self, dt):
2969 return self.dstvalue
2970
2971 cls = self.theclass
2972 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
2973 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
2974 t = d.timetuple()
2975 self.assertEqual(1, t.tm_year)
2976 self.assertEqual(1, t.tm_mon)
2977 self.assertEqual(1, t.tm_mday)
2978 self.assertEqual(10, t.tm_hour)
2979 self.assertEqual(20, t.tm_min)
2980 self.assertEqual(30, t.tm_sec)
2981 self.assertEqual(0, t.tm_wday)
2982 self.assertEqual(1, t.tm_yday)
2983 self.assertEqual(flag, t.tm_isdst)
2984
2985 # dst() returns wrong type.
2986 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
2987
2988 # dst() at the edge.
2989 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
2990 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
2991
2992 # dst() out of range.
2993 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
2994 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
2995
2996 def test_utctimetuple(self):
2997 class DST(tzinfo):
2998 def __init__(self, dstvalue):
Tim Peters397301e2003-01-02 21:28:08 +00002999 if isinstance(dstvalue, int):
3000 dstvalue = timedelta(minutes=dstvalue)
Tim Peters2a799bf2002-12-16 20:18:38 +00003001 self.dstvalue = dstvalue
3002 def dst(self, dt):
3003 return self.dstvalue
3004
3005 cls = self.theclass
3006 # This can't work: DST didn't implement utcoffset.
3007 self.assertRaises(NotImplementedError,
3008 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3009
3010 class UOFS(DST):
3011 def __init__(self, uofs, dofs=None):
3012 DST.__init__(self, dofs)
Tim Peters397301e2003-01-02 21:28:08 +00003013 self.uofs = timedelta(minutes=uofs)
Tim Peters2a799bf2002-12-16 20:18:38 +00003014 def utcoffset(self, dt):
3015 return self.uofs
3016
Tim Peters2a799bf2002-12-16 20:18:38 +00003017 for dstvalue in -33, 33, 0, None:
3018 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3019 t = d.utctimetuple()
3020 self.assertEqual(d.year, t.tm_year)
3021 self.assertEqual(d.month, t.tm_mon)
3022 self.assertEqual(d.day, t.tm_mday)
3023 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3024 self.assertEqual(13, t.tm_min)
3025 self.assertEqual(d.second, t.tm_sec)
3026 self.assertEqual(d.weekday(), t.tm_wday)
3027 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3028 t.tm_yday)
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003029 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3030 # is never in effect for a UTC time.
Tim Peters2a799bf2002-12-16 20:18:38 +00003031 self.assertEqual(0, t.tm_isdst)
3032
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003033 # Check that utctimetuple() is the same as
3034 # astimezone(utc).timetuple()
3035 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3036 for tz in [timezone.min, timezone.utc, timezone.max]:
3037 dtz = d.replace(tzinfo=tz)
3038 self.assertEqual(dtz.utctimetuple()[:-1],
3039 dtz.astimezone(timezone.utc).timetuple()[:-1])
3040 # At the edges, UTC adjustment can produce years out-of-range
3041 # for a datetime object. Ensure that an OverflowError is
3042 # raised.
Tim Peters2a799bf2002-12-16 20:18:38 +00003043 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3044 # That goes back 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003045 self.assertRaises(OverflowError, tiny.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003046
3047 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3048 # That goes forward 1 minute less than a full day.
Alexander Belopolsky75f94c22010-06-21 15:21:14 +00003049 self.assertRaises(OverflowError, huge.utctimetuple)
3050 # More overflow cases
3051 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3052 self.assertRaises(OverflowError, tiny.utctimetuple)
3053 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3054 self.assertRaises(OverflowError, huge.utctimetuple)
Tim Peters2a799bf2002-12-16 20:18:38 +00003055
3056 def test_tzinfo_isoformat(self):
3057 zero = FixedOffset(0, "+00:00")
3058 plus = FixedOffset(220, "+03:40")
3059 minus = FixedOffset(-231, "-03:51")
3060 unknown = FixedOffset(None, "")
3061
3062 cls = self.theclass
3063 datestr = '0001-02-03'
3064 for ofs in None, zero, plus, minus, unknown:
Tim Peters6578dc92002-12-24 18:31:27 +00003065 for us in 0, 987001:
Tim Peters2a799bf2002-12-16 20:18:38 +00003066 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3067 timestr = '04:05:59' + (us and '.987001' or '')
3068 ofsstr = ofs is not None and d.tzname() or ''
3069 tailstr = timestr + ofsstr
3070 iso = d.isoformat()
3071 self.assertEqual(iso, datestr + 'T' + tailstr)
3072 self.assertEqual(iso, d.isoformat('T'))
3073 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
Walter Dörwaldbc1f8862007-06-20 11:02:38 +00003074 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
Tim Peters2a799bf2002-12-16 20:18:38 +00003075 self.assertEqual(str(d), datestr + ' ' + tailstr)
3076
Tim Peters12bf3392002-12-24 05:41:27 +00003077 def test_replace(self):
3078 cls = self.theclass
3079 z100 = FixedOffset(100, "+100")
3080 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3081 args = [1, 2, 3, 4, 5, 6, 7, z100]
3082 base = cls(*args)
3083 self.assertEqual(base, base.replace())
3084
3085 i = 0
3086 for name, newval in (("year", 2),
3087 ("month", 3),
3088 ("day", 4),
3089 ("hour", 5),
3090 ("minute", 6),
3091 ("second", 7),
3092 ("microsecond", 8),
3093 ("tzinfo", zm200)):
3094 newargs = args[:]
3095 newargs[i] = newval
3096 expected = cls(*newargs)
3097 got = base.replace(**{name: newval})
3098 self.assertEqual(expected, got)
3099 i += 1
3100
3101 # Ensure we can get rid of a tzinfo.
3102 self.assertEqual(base.tzname(), "+100")
3103 base2 = base.replace(tzinfo=None)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003104 self.assertTrue(base2.tzinfo is None)
3105 self.assertTrue(base2.tzname() is None)
Tim Peters12bf3392002-12-24 05:41:27 +00003106
3107 # Ensure we can add one.
3108 base3 = base2.replace(tzinfo=z100)
3109 self.assertEqual(base, base3)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003110 self.assertTrue(base.tzinfo is base3.tzinfo)
Tim Peters12bf3392002-12-24 05:41:27 +00003111
3112 # Out of bounds.
3113 base = cls(2000, 2, 29)
3114 self.assertRaises(ValueError, base.replace, year=2001)
Tim Peters2a799bf2002-12-16 20:18:38 +00003115
Tim Peters80475bb2002-12-25 07:40:55 +00003116 def test_more_astimezone(self):
3117 # The inherited test_astimezone covered some trivial and error cases.
3118 fnone = FixedOffset(None, "None")
3119 f44m = FixedOffset(44, "44")
3120 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3121
Tim Peters10cadce2003-01-23 19:58:02 +00003122 dt = self.theclass.now(tz=f44m)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003123 self.assertTrue(dt.tzinfo is f44m)
Tim Peters52dcce22003-01-23 16:36:11 +00003124 # Replacing with degenerate tzinfo raises an exception.
3125 self.assertRaises(ValueError, dt.astimezone, fnone)
3126 # Ditto with None tz.
3127 self.assertRaises(TypeError, dt.astimezone, None)
3128 # Replacing with same tzinfo makes no change.
Tim Peters80475bb2002-12-25 07:40:55 +00003129 x = dt.astimezone(dt.tzinfo)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003130 self.assertTrue(x.tzinfo is f44m)
Tim Peters80475bb2002-12-25 07:40:55 +00003131 self.assertEqual(x.date(), dt.date())
3132 self.assertEqual(x.time(), dt.time())
3133
3134 # Replacing with different tzinfo does adjust.
3135 got = dt.astimezone(fm5h)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003136 self.assertTrue(got.tzinfo is fm5h)
Tim Peters80475bb2002-12-25 07:40:55 +00003137 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3138 expected = dt - dt.utcoffset() # in effect, convert to UTC
3139 expected += fm5h.utcoffset(dt) # and from there to local time
3140 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3141 self.assertEqual(got.date(), expected.date())
3142 self.assertEqual(got.time(), expected.time())
3143 self.assertEqual(got.timetz(), expected.timetz())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003144 self.assertTrue(got.tzinfo is expected.tzinfo)
Tim Peters80475bb2002-12-25 07:40:55 +00003145 self.assertEqual(got, expected)
3146
Tim Peters4c0db782002-12-26 05:01:19 +00003147 def test_aware_subtract(self):
3148 cls = self.theclass
3149
Tim Peters60c76e42002-12-27 00:41:11 +00003150 # Ensure that utcoffset() is ignored when the operands have the
3151 # same tzinfo member.
Tim Peters4c0db782002-12-26 05:01:19 +00003152 class OperandDependentOffset(tzinfo):
3153 def utcoffset(self, t):
3154 if t.minute < 10:
Tim Peters397301e2003-01-02 21:28:08 +00003155 # d0 and d1 equal after adjustment
3156 return timedelta(minutes=t.minute)
Tim Peters4c0db782002-12-26 05:01:19 +00003157 else:
Tim Peters397301e2003-01-02 21:28:08 +00003158 # d2 off in the weeds
3159 return timedelta(minutes=59)
Tim Peters4c0db782002-12-26 05:01:19 +00003160
3161 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3162 d0 = base.replace(minute=3)
3163 d1 = base.replace(minute=9)
3164 d2 = base.replace(minute=11)
3165 for x in d0, d1, d2:
3166 for y in d0, d1, d2:
3167 got = x - y
Tim Peters60c76e42002-12-27 00:41:11 +00003168 expected = timedelta(minutes=x.minute - y.minute)
3169 self.assertEqual(got, expected)
3170
3171 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3172 # ignored.
3173 base = cls(8, 9, 10, 11, 12, 13, 14)
3174 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3175 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3176 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3177 for x in d0, d1, d2:
3178 for y in d0, d1, d2:
3179 got = x - y
Tim Peters4c0db782002-12-26 05:01:19 +00003180 if (x is d0 or x is d1) and (y is d0 or y is d1):
3181 expected = timedelta(0)
3182 elif x is y is d2:
3183 expected = timedelta(0)
3184 elif x is d2:
3185 expected = timedelta(minutes=(11-59)-0)
3186 else:
3187 assert y is d2
3188 expected = timedelta(minutes=0-(11-59))
3189 self.assertEqual(got, expected)
3190
Tim Peters60c76e42002-12-27 00:41:11 +00003191 def test_mixed_compare(self):
3192 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters0bf60bd2003-01-08 20:40:01 +00003193 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
Tim Peters60c76e42002-12-27 00:41:11 +00003194 self.assertEqual(t1, t2)
3195 t2 = t2.replace(tzinfo=None)
3196 self.assertEqual(t1, t2)
3197 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3198 self.assertEqual(t1, t2)
Tim Peters68124bb2003-02-08 03:46:31 +00003199 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
3200 self.assertRaises(TypeError, lambda: t1 == t2)
Tim Peters60c76e42002-12-27 00:41:11 +00003201
Tim Peters0bf60bd2003-01-08 20:40:01 +00003202 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
Tim Peters60c76e42002-12-27 00:41:11 +00003203 class Varies(tzinfo):
3204 def __init__(self):
Tim Peters397301e2003-01-02 21:28:08 +00003205 self.offset = timedelta(minutes=22)
Tim Peters60c76e42002-12-27 00:41:11 +00003206 def utcoffset(self, t):
Tim Peters397301e2003-01-02 21:28:08 +00003207 self.offset += timedelta(minutes=1)
Tim Peters60c76e42002-12-27 00:41:11 +00003208 return self.offset
3209
3210 v = Varies()
3211 t1 = t2.replace(tzinfo=v)
3212 t2 = t2.replace(tzinfo=v)
3213 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3214 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3215 self.assertEqual(t1, t2)
3216
3217 # But if they're not identical, it isn't ignored.
3218 t2 = t2.replace(tzinfo=Varies())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003219 self.assertTrue(t1 < t2) # t1's offset counter still going up
Tim Peters80475bb2002-12-25 07:40:55 +00003220
Tim Petersa98924a2003-05-17 05:55:19 +00003221 def test_subclass_datetimetz(self):
3222
3223 class C(self.theclass):
3224 theAnswer = 42
3225
3226 def __new__(cls, *args, **kws):
3227 temp = kws.copy()
3228 extra = temp.pop('extra')
3229 result = self.theclass.__new__(cls, *args, **temp)
3230 result.extra = extra
3231 return result
3232
3233 def newmeth(self, start):
3234 return start + self.hour + self.year
3235
3236 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3237
3238 dt1 = self.theclass(*args)
3239 dt2 = C(*args, **{'extra': 7})
3240
3241 self.assertEqual(dt2.__class__, C)
3242 self.assertEqual(dt2.theAnswer, 42)
3243 self.assertEqual(dt2.extra, 7)
3244 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3245 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3246
Tim Peters621818b2002-12-29 23:44:49 +00003247# Pain to set up DST-aware tzinfo classes.
3248
3249def first_sunday_on_or_after(dt):
3250 days_to_go = 6 - dt.weekday()
3251 if days_to_go:
3252 dt += timedelta(days_to_go)
3253 return dt
3254
3255ZERO = timedelta(0)
Alexander Belopolskyca94f552010-06-17 18:30:34 +00003256MINUTE = timedelta(minutes=1)
Tim Peters621818b2002-12-29 23:44:49 +00003257HOUR = timedelta(hours=1)
3258DAY = timedelta(days=1)
3259# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3260DSTSTART = datetime(1, 4, 1, 2)
3261# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
Tim Peters327098a2003-01-20 22:54:38 +00003262# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3263# being standard time on that day, there is no spelling in local time of
3264# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3265DSTEND = datetime(1, 10, 25, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003266
3267class USTimeZone(tzinfo):
3268
3269 def __init__(self, hours, reprname, stdname, dstname):
3270 self.stdoffset = timedelta(hours=hours)
3271 self.reprname = reprname
3272 self.stdname = stdname
3273 self.dstname = dstname
3274
3275 def __repr__(self):
3276 return self.reprname
3277
3278 def tzname(self, dt):
3279 if self.dst(dt):
3280 return self.dstname
3281 else:
3282 return self.stdname
3283
3284 def utcoffset(self, dt):
3285 return self.stdoffset + self.dst(dt)
3286
3287 def dst(self, dt):
Tim Petersbad8ff02002-12-30 20:52:32 +00003288 if dt is None or dt.tzinfo is None:
Tim Peters621818b2002-12-29 23:44:49 +00003289 # An exception instead may be sensible here, in one or more of
3290 # the cases.
3291 return ZERO
Tim Peters521fc152002-12-31 17:36:56 +00003292 assert dt.tzinfo is self
Tim Peters621818b2002-12-29 23:44:49 +00003293
3294 # Find first Sunday in April.
3295 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3296 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3297
3298 # Find last Sunday in October.
3299 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3300 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3301
Tim Peters621818b2002-12-29 23:44:49 +00003302 # Can't compare naive to aware objects, so strip the timezone from
3303 # dt first.
Tim Peters52dcce22003-01-23 16:36:11 +00003304 if start <= dt.replace(tzinfo=None) < end:
Tim Peters621818b2002-12-29 23:44:49 +00003305 return HOUR
3306 else:
3307 return ZERO
3308
Tim Peters521fc152002-12-31 17:36:56 +00003309Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3310Central = USTimeZone(-6, "Central", "CST", "CDT")
3311Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3312Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
Tim Peters1024bf82002-12-30 17:09:40 +00003313utc_real = FixedOffset(0, "UTC", 0)
3314# For better test coverage, we want another flavor of UTC that's west of
3315# the Eastern and Pacific timezones.
Tim Petersadf64202003-01-04 06:03:15 +00003316utc_fake = FixedOffset(-12*60, "UTCfake", 0)
Tim Peters621818b2002-12-29 23:44:49 +00003317
3318class TestTimezoneConversions(unittest.TestCase):
Tim Peters327098a2003-01-20 22:54:38 +00003319 # The DST switch times for 2002, in std time.
Tim Peters0bf60bd2003-01-08 20:40:01 +00003320 dston = datetime(2002, 4, 7, 2)
Tim Peters327098a2003-01-20 22:54:38 +00003321 dstoff = datetime(2002, 10, 27, 1)
Tim Peters621818b2002-12-29 23:44:49 +00003322
Tim Peters0bf60bd2003-01-08 20:40:01 +00003323 theclass = datetime
Tim Peters710fb152003-01-02 19:35:54 +00003324
Tim Peters521fc152002-12-31 17:36:56 +00003325 # Check a time that's inside DST.
3326 def checkinside(self, dt, tz, utc, dston, dstoff):
3327 self.assertEqual(dt.dst(), HOUR)
3328
3329 # Conversion to our own timezone is always an identity.
3330 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters521fc152002-12-31 17:36:56 +00003331
3332 asutc = dt.astimezone(utc)
3333 there_and_back = asutc.astimezone(tz)
3334
3335 # Conversion to UTC and back isn't always an identity here,
3336 # because there are redundant spellings (in local time) of
3337 # UTC time when DST begins: the clock jumps from 1:59:59
3338 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3339 # make sense then. The classes above treat 2:MM:SS as
3340 # daylight time then (it's "after 2am"), really an alias
3341 # for 1:MM:SS standard time. The latter form is what
3342 # conversion back from UTC produces.
3343 if dt.date() == dston.date() and dt.hour == 2:
3344 # We're in the redundant hour, and coming back from
3345 # UTC gives the 1:MM:SS standard-time spelling.
3346 self.assertEqual(there_and_back + HOUR, dt)
3347 # Although during was considered to be in daylight
3348 # time, there_and_back is not.
3349 self.assertEqual(there_and_back.dst(), ZERO)
3350 # They're the same times in UTC.
3351 self.assertEqual(there_and_back.astimezone(utc),
3352 dt.astimezone(utc))
3353 else:
3354 # We're not in the redundant hour.
3355 self.assertEqual(dt, there_and_back)
3356
Tim Peters327098a2003-01-20 22:54:38 +00003357 # Because we have a redundant spelling when DST begins, there is
3358 # (unforunately) an hour when DST ends that can't be spelled at all in
3359 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3360 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3361 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3362 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3363 # expressed in local time. Nevertheless, we want conversion back
3364 # from UTC to mimic the local clock's "repeat an hour" behavior.
Tim Peters521fc152002-12-31 17:36:56 +00003365 nexthour_utc = asutc + HOUR
Tim Petersadf64202003-01-04 06:03:15 +00003366 nexthour_tz = nexthour_utc.astimezone(tz)
Tim Peters327098a2003-01-20 22:54:38 +00003367 if dt.date() == dstoff.date() and dt.hour == 0:
3368 # We're in the hour before the last DST hour. The last DST hour
Tim Petersadf64202003-01-04 06:03:15 +00003369 # is ineffable. We want the conversion back to repeat 1:MM.
Tim Peters327098a2003-01-20 22:54:38 +00003370 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3371 nexthour_utc += HOUR
3372 nexthour_tz = nexthour_utc.astimezone(tz)
3373 self.assertEqual(nexthour_tz, dt.replace(hour=1))
Tim Peters521fc152002-12-31 17:36:56 +00003374 else:
Tim Peters327098a2003-01-20 22:54:38 +00003375 self.assertEqual(nexthour_tz - dt, HOUR)
Tim Peters521fc152002-12-31 17:36:56 +00003376
3377 # Check a time that's outside DST.
3378 def checkoutside(self, dt, tz, utc):
3379 self.assertEqual(dt.dst(), ZERO)
3380
3381 # Conversion to our own timezone is always an identity.
3382 self.assertEqual(dt.astimezone(tz), dt)
Tim Peters52dcce22003-01-23 16:36:11 +00003383
3384 # Converting to UTC and back is an identity too.
3385 asutc = dt.astimezone(utc)
3386 there_and_back = asutc.astimezone(tz)
3387 self.assertEqual(dt, there_and_back)
Tim Peters521fc152002-12-31 17:36:56 +00003388
Tim Peters1024bf82002-12-30 17:09:40 +00003389 def convert_between_tz_and_utc(self, tz, utc):
3390 dston = self.dston.replace(tzinfo=tz)
Tim Peters327098a2003-01-20 22:54:38 +00003391 # Because 1:MM on the day DST ends is taken as being standard time,
3392 # there is no spelling in tz for the last hour of daylight time.
3393 # For purposes of the test, the last hour of DST is 0:MM, which is
3394 # taken as being daylight time (and 1:MM is taken as being standard
3395 # time).
Tim Peters1024bf82002-12-30 17:09:40 +00003396 dstoff = self.dstoff.replace(tzinfo=tz)
3397 for delta in (timedelta(weeks=13),
3398 DAY,
3399 HOUR,
3400 timedelta(minutes=1),
3401 timedelta(microseconds=1)):
3402
Tim Peters521fc152002-12-31 17:36:56 +00003403 self.checkinside(dston, tz, utc, dston, dstoff)
3404 for during in dston + delta, dstoff - delta:
3405 self.checkinside(during, tz, utc, dston, dstoff)
Tim Peters31cc3152002-12-30 17:37:30 +00003406
Tim Peters521fc152002-12-31 17:36:56 +00003407 self.checkoutside(dstoff, tz, utc)
3408 for outside in dston - delta, dstoff + delta:
3409 self.checkoutside(outside, tz, utc)
Tim Peters31cc3152002-12-30 17:37:30 +00003410
Tim Peters621818b2002-12-29 23:44:49 +00003411 def test_easy(self):
3412 # Despite the name of this test, the endcases are excruciating.
Tim Peters1024bf82002-12-30 17:09:40 +00003413 self.convert_between_tz_and_utc(Eastern, utc_real)
3414 self.convert_between_tz_and_utc(Pacific, utc_real)
3415 self.convert_between_tz_and_utc(Eastern, utc_fake)
3416 self.convert_between_tz_and_utc(Pacific, utc_fake)
3417 # The next is really dancing near the edge. It works because
3418 # Pacific and Eastern are far enough apart that their "problem
3419 # hours" don't overlap.
3420 self.convert_between_tz_and_utc(Eastern, Pacific)
3421 self.convert_between_tz_and_utc(Pacific, Eastern)
Tim Peters36087ed2003-01-01 04:18:51 +00003422 # OTOH, these fail! Don't enable them. The difficulty is that
3423 # the edge case tests assume that every hour is representable in
3424 # the "utc" class. This is always true for a fixed-offset tzinfo
3425 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3426 # For these adjacent DST-aware time zones, the range of time offsets
3427 # tested ends up creating hours in the one that aren't representable
3428 # in the other. For the same reason, we would see failures in the
3429 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3430 # offset deltas in convert_between_tz_and_utc().
3431 #
3432 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3433 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
Tim Peters621818b2002-12-29 23:44:49 +00003434
Tim Petersf3615152003-01-01 21:51:37 +00003435 def test_tricky(self):
3436 # 22:00 on day before daylight starts.
3437 fourback = self.dston - timedelta(hours=4)
3438 ninewest = FixedOffset(-9*60, "-0900", 0)
Tim Peters52dcce22003-01-23 16:36:11 +00003439 fourback = fourback.replace(tzinfo=ninewest)
Tim Petersf3615152003-01-01 21:51:37 +00003440 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3441 # 2", we should get the 3 spelling.
3442 # If we plug 22:00 the day before into Eastern, it "looks like std
3443 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3444 # to 22:00 lands on 2:00, which makes no sense in local time (the
3445 # local clock jumps from 1 to 3). The point here is to make sure we
3446 # get the 3 spelling.
3447 expected = self.dston.replace(hour=3)
Tim Peters52dcce22003-01-23 16:36:11 +00003448 got = fourback.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003449 self.assertEqual(expected, got)
3450
3451 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3452 # case we want the 1:00 spelling.
Tim Peters52dcce22003-01-23 16:36:11 +00003453 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
Tim Petersf3615152003-01-01 21:51:37 +00003454 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3455 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3456 # spelling.
3457 expected = self.dston.replace(hour=1)
Tim Peters52dcce22003-01-23 16:36:11 +00003458 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
Tim Petersf3615152003-01-01 21:51:37 +00003459 self.assertEqual(expected, got)
Tim Peters621818b2002-12-29 23:44:49 +00003460
Tim Petersadf64202003-01-04 06:03:15 +00003461 # Now on the day DST ends, we want "repeat an hour" behavior.
3462 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3463 # EST 23:MM 0:MM 1:MM 2:MM
3464 # EDT 0:MM 1:MM 2:MM 3:MM
3465 # wall 0:MM 1:MM 1:MM 2:MM against these
3466 for utc in utc_real, utc_fake:
3467 for tz in Eastern, Pacific:
Tim Peters327098a2003-01-20 22:54:38 +00003468 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
Tim Petersadf64202003-01-04 06:03:15 +00003469 # Convert that to UTC.
3470 first_std_hour -= tz.utcoffset(None)
3471 # Adjust for possibly fake UTC.
3472 asutc = first_std_hour + utc.utcoffset(None)
3473 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3474 # tz=Eastern.
3475 asutcbase = asutc.replace(tzinfo=utc)
3476 for tzhour in (0, 1, 1, 2):
3477 expectedbase = self.dstoff.replace(hour=tzhour)
3478 for minute in 0, 30, 59:
3479 expected = expectedbase.replace(minute=minute)
3480 asutc = asutcbase.replace(minute=minute)
3481 astz = asutc.astimezone(tz)
3482 self.assertEqual(astz.replace(tzinfo=None), expected)
3483 asutcbase += HOUR
3484
3485
Tim Peters710fb152003-01-02 19:35:54 +00003486 def test_bogus_dst(self):
3487 class ok(tzinfo):
3488 def utcoffset(self, dt): return HOUR
3489 def dst(self, dt): return HOUR
3490
3491 now = self.theclass.now().replace(tzinfo=utc_real)
3492 # Doesn't blow up.
3493 now.astimezone(ok())
3494
3495 # Does blow up.
3496 class notok(ok):
3497 def dst(self, dt): return None
3498 self.assertRaises(ValueError, now.astimezone, notok())
3499
Alexander Belopolsky1b402922010-06-22 14:07:33 +00003500 # Sometimes blow up. In the following, tzinfo.dst()
3501 # implementation may return None or not Nonedepending on
3502 # whether DST is assumed to be in effect. In this situation,
3503 # a ValueError should be raised by astimezone().
3504 class tricky_notok(ok):
3505 def dst(self, dt):
3506 if dt.year == 2000:
3507 return None
3508 else:
3509 return 10*HOUR
3510 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3511 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3512
Tim Peters52dcce22003-01-23 16:36:11 +00003513 def test_fromutc(self):
3514 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3515 now = datetime.utcnow().replace(tzinfo=utc_real)
3516 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3517 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3518 enow = Eastern.fromutc(now) # doesn't blow up
3519 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3520 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3521 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3522
3523 # Always converts UTC to standard time.
3524 class FauxUSTimeZone(USTimeZone):
3525 def fromutc(self, dt):
3526 return dt + self.stdoffset
3527 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3528
3529 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3530 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3531 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3532
3533 # Check around DST start.
3534 start = self.dston.replace(hour=4, tzinfo=Eastern)
3535 fstart = start.replace(tzinfo=FEastern)
3536 for wall in 23, 0, 1, 3, 4, 5:
3537 expected = start.replace(hour=wall)
3538 if wall == 23:
3539 expected -= timedelta(days=1)
3540 got = Eastern.fromutc(start)
3541 self.assertEqual(expected, got)
3542
3543 expected = fstart + FEastern.stdoffset
3544 got = FEastern.fromutc(fstart)
3545 self.assertEqual(expected, got)
3546
3547 # Ensure astimezone() calls fromutc() too.
3548 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3549 self.assertEqual(expected, got)
3550
3551 start += HOUR
3552 fstart += HOUR
3553
3554 # Check around DST end.
3555 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3556 fstart = start.replace(tzinfo=FEastern)
3557 for wall in 0, 1, 1, 2, 3, 4:
3558 expected = start.replace(hour=wall)
3559 got = Eastern.fromutc(start)
3560 self.assertEqual(expected, got)
3561
3562 expected = fstart + FEastern.stdoffset
3563 got = FEastern.fromutc(fstart)
3564 self.assertEqual(expected, got)
3565
3566 # Ensure astimezone() calls fromutc() too.
3567 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3568 self.assertEqual(expected, got)
3569
3570 start += HOUR
3571 fstart += HOUR
3572
Tim Peters710fb152003-01-02 19:35:54 +00003573
Tim Peters528ca532004-09-16 01:30:50 +00003574#############################################################################
3575# oddballs
3576
3577class Oddballs(unittest.TestCase):
3578
3579 def test_bug_1028306(self):
3580 # Trying to compare a date to a datetime should act like a mixed-
3581 # type comparison, despite that datetime is a subclass of date.
3582 as_date = date.today()
3583 as_datetime = datetime.combine(as_date, time())
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00003584 self.assertTrue(as_date != as_datetime)
3585 self.assertTrue(as_datetime != as_date)
3586 self.assertTrue(not as_date == as_datetime)
3587 self.assertTrue(not as_datetime == as_date)
Tim Peters528ca532004-09-16 01:30:50 +00003588 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3589 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3590 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3591 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3592 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3593 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3594 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3595 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3596
3597 # Neverthelss, comparison should work with the base-class (date)
3598 # projection if use of a date method is forced.
Guido van Rossum19960592006-08-24 17:29:38 +00003599 self.assertEqual(as_date.__eq__(as_datetime), True)
Tim Peters528ca532004-09-16 01:30:50 +00003600 different_day = (as_date.day + 1) % 20 + 1
Guido van Rossum19960592006-08-24 17:29:38 +00003601 as_different = as_datetime.replace(day= different_day)
3602 self.assertEqual(as_date.__eq__(as_different), False)
Tim Peters528ca532004-09-16 01:30:50 +00003603
3604 # And date should compare with other subclasses of date. If a
3605 # subclass wants to stop this, it's up to the subclass to do so.
3606 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3607 self.assertEqual(as_date, date_sc)
3608 self.assertEqual(date_sc, as_date)
3609
3610 # Ditto for datetimes.
3611 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3612 as_date.day, 0, 0, 0)
3613 self.assertEqual(as_datetime, datetime_sc)
3614 self.assertEqual(datetime_sc, as_datetime)
3615
Tim Peters2a799bf2002-12-16 20:18:38 +00003616def test_main():
Benjamin Petersonee8712c2008-05-20 21:35:26 +00003617 support.run_unittest(__name__)
Tim Peters2a799bf2002-12-16 20:18:38 +00003618
3619if __name__ == "__main__":
3620 test_main()