blob: 564ff5224397d4b8073075b0fcb868638ff0c7cf [file] [log] [blame]
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001"""Test date/time type.
2
3See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4"""
5
Antoine Pitrou392f4132014-10-03 11:25:30 +02006import decimal
Alexander Belopolskycf86e362010-07-23 19:25:47 +00007import sys
8import pickle
Raymond Hettinger5a2146a2014-07-25 14:59:48 -07009import random
Alexander Belopolskycf86e362010-07-23 19:25:47 +000010import unittest
11
12from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
13
14from test import support
15
16import datetime as datetime_module
17from datetime import MINYEAR, MAXYEAR
18from datetime import timedelta
19from datetime import tzinfo
20from datetime import time
21from datetime import timezone
22from datetime import date, datetime
23import time as _time
24
25# Needed by test_datetime
26import _strptime
27#
28
29
30pickle_choices = [(pickle, pickle, proto)
31 for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
32assert len(pickle_choices) == pickle.HIGHEST_PROTOCOL + 1
33
34# An arbitrary collection of objects of non-datetime types, for testing
35# mixed-type comparisons.
36OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
37
38
39# XXX Copied from test_float.
40INF = float("inf")
41NAN = float("nan")
42
Alexander Belopolskycf86e362010-07-23 19:25:47 +000043
44#############################################################################
45# module tests
46
47class TestModule(unittest.TestCase):
48
49 def test_constants(self):
50 datetime = datetime_module
51 self.assertEqual(datetime.MINYEAR, 1)
52 self.assertEqual(datetime.MAXYEAR, 9999)
53
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -040054 def test_name_cleanup(self):
55 if '_Fast' not in str(self):
56 return
57 datetime = datetime_module
58 names = set(name for name in dir(datetime)
59 if not name.startswith('__') and not name.endswith('__'))
60 allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
61 'datetime_CAPI', 'time', 'timedelta', 'timezone',
62 'tzinfo'])
63 self.assertEqual(names - allowed, set([]))
64
Alexander Belopolskycf86e362010-07-23 19:25:47 +000065#############################################################################
66# tzinfo tests
67
68class FixedOffset(tzinfo):
69
70 def __init__(self, offset, name, dstoffset=42):
71 if isinstance(offset, int):
72 offset = timedelta(minutes=offset)
73 if isinstance(dstoffset, int):
74 dstoffset = timedelta(minutes=dstoffset)
75 self.__offset = offset
76 self.__name = name
77 self.__dstoffset = dstoffset
78 def __repr__(self):
79 return self.__name.lower()
80 def utcoffset(self, dt):
81 return self.__offset
82 def tzname(self, dt):
83 return self.__name
84 def dst(self, dt):
85 return self.__dstoffset
86
87class PicklableFixedOffset(FixedOffset):
88
89 def __init__(self, offset=None, name=None, dstoffset=None):
90 FixedOffset.__init__(self, offset, name, dstoffset)
91
Raymond Hettinger5a2146a2014-07-25 14:59:48 -070092class _TZInfo(tzinfo):
93 def utcoffset(self, datetime_module):
94 return random.random()
95
Alexander Belopolskycf86e362010-07-23 19:25:47 +000096class TestTZInfo(unittest.TestCase):
97
Raymond Hettinger5a2146a2014-07-25 14:59:48 -070098 def test_refcnt_crash_bug_22044(self):
99 tz1 = _TZInfo()
100 dt1 = datetime(2014, 7, 21, 11, 32, 3, 0, tz1)
101 with self.assertRaises(TypeError):
102 dt1.utcoffset()
103
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000104 def test_non_abstractness(self):
105 # In order to allow subclasses to get pickled, the C implementation
106 # wasn't able to get away with having __init__ raise
107 # NotImplementedError.
108 useless = tzinfo()
109 dt = datetime.max
110 self.assertRaises(NotImplementedError, useless.tzname, dt)
111 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
112 self.assertRaises(NotImplementedError, useless.dst, dt)
113
114 def test_subclass_must_override(self):
115 class NotEnough(tzinfo):
116 def __init__(self, offset, name):
117 self.__offset = offset
118 self.__name = name
119 self.assertTrue(issubclass(NotEnough, tzinfo))
120 ne = NotEnough(3, "NotByALongShot")
121 self.assertIsInstance(ne, tzinfo)
122
123 dt = datetime.now()
124 self.assertRaises(NotImplementedError, ne.tzname, dt)
125 self.assertRaises(NotImplementedError, ne.utcoffset, dt)
126 self.assertRaises(NotImplementedError, ne.dst, dt)
127
128 def test_normal(self):
129 fo = FixedOffset(3, "Three")
130 self.assertIsInstance(fo, tzinfo)
131 for dt in datetime.now(), None:
132 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
133 self.assertEqual(fo.tzname(dt), "Three")
134 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
135
136 def test_pickling_base(self):
137 # There's no point to pickling tzinfo objects on their own (they
138 # carry no data), but they need to be picklable anyway else
139 # concrete subclasses can't be pickled.
140 orig = tzinfo.__new__(tzinfo)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200141 self.assertIs(type(orig), tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000142 for pickler, unpickler, proto in pickle_choices:
143 green = pickler.dumps(orig, proto)
144 derived = unpickler.loads(green)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200145 self.assertIs(type(derived), tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000146
147 def test_pickling_subclass(self):
148 # Make sure we can pickle/unpickle an instance of a subclass.
149 offset = timedelta(minutes=-300)
150 for otype, args in [
151 (PicklableFixedOffset, (offset, 'cookie')),
152 (timezone, (offset,)),
153 (timezone, (offset, "EST"))]:
154 orig = otype(*args)
155 oname = orig.tzname(None)
156 self.assertIsInstance(orig, tzinfo)
157 self.assertIs(type(orig), otype)
158 self.assertEqual(orig.utcoffset(None), offset)
159 self.assertEqual(orig.tzname(None), oname)
160 for pickler, unpickler, proto in pickle_choices:
161 green = pickler.dumps(orig, proto)
162 derived = unpickler.loads(green)
163 self.assertIsInstance(derived, tzinfo)
164 self.assertIs(type(derived), otype)
165 self.assertEqual(derived.utcoffset(None), offset)
166 self.assertEqual(derived.tzname(None), oname)
167
168class TestTimeZone(unittest.TestCase):
169
170 def setUp(self):
171 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
172 self.EST = timezone(-timedelta(hours=5), 'EST')
173 self.DT = datetime(2010, 1, 1)
174
175 def test_str(self):
176 for tz in [self.ACDT, self.EST, timezone.utc,
177 timezone.min, timezone.max]:
178 self.assertEqual(str(tz), tz.tzname(None))
179
180 def test_repr(self):
181 datetime = datetime_module
182 for tz in [self.ACDT, self.EST, timezone.utc,
183 timezone.min, timezone.max]:
184 # test round-trip
185 tzrep = repr(tz)
186 self.assertEqual(tz, eval(tzrep))
187
188
189 def test_class_members(self):
190 limit = timedelta(hours=23, minutes=59)
191 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
192 self.assertEqual(timezone.min.utcoffset(None), -limit)
193 self.assertEqual(timezone.max.utcoffset(None), limit)
194
195
196 def test_constructor(self):
Alexander Belopolsky1bcbaab2010-10-14 17:03:51 +0000197 self.assertIs(timezone.utc, timezone(timedelta(0)))
198 self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
199 self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000200 # invalid offsets
201 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
202 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
203 self.assertRaises(ValueError, timezone, invalid)
204 self.assertRaises(ValueError, timezone, -invalid)
205
206 with self.assertRaises(TypeError): timezone(None)
207 with self.assertRaises(TypeError): timezone(42)
208 with self.assertRaises(TypeError): timezone(ZERO, None)
209 with self.assertRaises(TypeError): timezone(ZERO, 42)
210 with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
211
212 def test_inheritance(self):
213 self.assertIsInstance(timezone.utc, tzinfo)
214 self.assertIsInstance(self.EST, tzinfo)
215
216 def test_utcoffset(self):
217 dummy = self.DT
218 for h in [0, 1.5, 12]:
219 offset = h * HOUR
220 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
221 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
222
223 with self.assertRaises(TypeError): self.EST.utcoffset('')
224 with self.assertRaises(TypeError): self.EST.utcoffset(5)
225
226
227 def test_dst(self):
Raymond Hettinger7beae8a2011-01-06 05:34:17 +0000228 self.assertIsNone(timezone.utc.dst(self.DT))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000229
230 with self.assertRaises(TypeError): self.EST.dst('')
231 with self.assertRaises(TypeError): self.EST.dst(5)
232
233 def test_tzname(self):
234 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None))
235 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
236 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
237 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
238 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
239
240 with self.assertRaises(TypeError): self.EST.tzname('')
241 with self.assertRaises(TypeError): self.EST.tzname(5)
242
243 def test_fromutc(self):
244 with self.assertRaises(ValueError):
245 timezone.utc.fromutc(self.DT)
246 with self.assertRaises(TypeError):
247 timezone.utc.fromutc('not datetime')
248 for tz in [self.EST, self.ACDT, Eastern]:
249 utctime = self.DT.replace(tzinfo=tz)
250 local = tz.fromutc(utctime)
251 self.assertEqual(local - utctime, tz.utcoffset(local))
252 self.assertEqual(local,
253 self.DT.replace(tzinfo=timezone.utc))
254
255 def test_comparison(self):
256 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
257 self.assertEqual(timezone(HOUR), timezone(HOUR))
258 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
259 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
260 self.assertIn(timezone(ZERO), {timezone(ZERO)})
Georg Brandl0085a242012-09-22 09:23:12 +0200261 self.assertTrue(timezone(ZERO) != None)
262 self.assertFalse(timezone(ZERO) == None)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000263
264 def test_aware_datetime(self):
265 # test that timezone instances can be used by datetime
266 t = datetime(1, 1, 1)
267 for tz in [timezone.min, timezone.max, timezone.utc]:
268 self.assertEqual(tz.tzname(t),
269 t.replace(tzinfo=tz).tzname())
270 self.assertEqual(tz.utcoffset(t),
271 t.replace(tzinfo=tz).utcoffset())
272 self.assertEqual(tz.dst(t),
273 t.replace(tzinfo=tz).dst())
274
275#############################################################################
Ezio Melotti85a86292013-08-17 16:57:41 +0300276# Base class for testing a particular aspect of timedelta, time, date and
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000277# datetime comparisons.
278
279class HarmlessMixedComparison:
280 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
281
282 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
283 # legit constructor.
284
285 def test_harmless_mixed_comparison(self):
286 me = self.theclass(1, 1, 1)
287
288 self.assertFalse(me == ())
289 self.assertTrue(me != ())
290 self.assertFalse(() == me)
291 self.assertTrue(() != me)
292
293 self.assertIn(me, [1, 20, [], me])
294 self.assertIn([], [me, 1, 20, []])
295
296 def test_harmful_mixed_comparison(self):
297 me = self.theclass(1, 1, 1)
298
299 self.assertRaises(TypeError, lambda: me < ())
300 self.assertRaises(TypeError, lambda: me <= ())
301 self.assertRaises(TypeError, lambda: me > ())
302 self.assertRaises(TypeError, lambda: me >= ())
303
304 self.assertRaises(TypeError, lambda: () < me)
305 self.assertRaises(TypeError, lambda: () <= me)
306 self.assertRaises(TypeError, lambda: () > me)
307 self.assertRaises(TypeError, lambda: () >= me)
308
309#############################################################################
310# timedelta tests
311
312class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
313
314 theclass = timedelta
315
316 def test_constructor(self):
317 eq = self.assertEqual
318 td = timedelta
319
320 # Check keyword args to constructor
321 eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
322 milliseconds=0, microseconds=0))
323 eq(td(1), td(days=1))
324 eq(td(0, 1), td(seconds=1))
325 eq(td(0, 0, 1), td(microseconds=1))
326 eq(td(weeks=1), td(days=7))
327 eq(td(days=1), td(hours=24))
328 eq(td(hours=1), td(minutes=60))
329 eq(td(minutes=1), td(seconds=60))
330 eq(td(seconds=1), td(milliseconds=1000))
331 eq(td(milliseconds=1), td(microseconds=1000))
332
333 # Check float args to constructor
334 eq(td(weeks=1.0/7), td(days=1))
335 eq(td(days=1.0/24), td(hours=1))
336 eq(td(hours=1.0/60), td(minutes=1))
337 eq(td(minutes=1.0/60), td(seconds=1))
338 eq(td(seconds=0.001), td(milliseconds=1))
339 eq(td(milliseconds=0.001), td(microseconds=1))
340
341 def test_computations(self):
342 eq = self.assertEqual
343 td = timedelta
344
345 a = td(7) # One week
346 b = td(0, 60) # One minute
347 c = td(0, 0, 1000) # One millisecond
348 eq(a+b+c, td(7, 60, 1000))
349 eq(a-b, td(6, 24*3600 - 60))
350 eq(b.__rsub__(a), td(6, 24*3600 - 60))
351 eq(-a, td(-7))
352 eq(+a, td(7))
353 eq(-b, td(-1, 24*3600 - 60))
354 eq(-c, td(-1, 24*3600 - 1, 999000))
355 eq(abs(a), a)
356 eq(abs(-a), a)
357 eq(td(6, 24*3600), a)
358 eq(td(0, 0, 60*1000000), b)
359 eq(a*10, td(70))
360 eq(a*10, 10*a)
361 eq(a*10, 10*a)
362 eq(b*10, td(0, 600))
363 eq(10*b, td(0, 600))
364 eq(b*10, td(0, 600))
365 eq(c*10, td(0, 0, 10000))
366 eq(10*c, td(0, 0, 10000))
367 eq(c*10, td(0, 0, 10000))
368 eq(a*-1, -a)
369 eq(b*-2, -b-b)
370 eq(c*-2, -c+-c)
371 eq(b*(60*24), (b*60)*24)
372 eq(b*(60*24), (60*b)*24)
373 eq(c*1000, td(0, 1))
374 eq(1000*c, td(0, 1))
375 eq(a//7, td(1))
376 eq(b//10, td(0, 6))
377 eq(c//1000, td(0, 0, 1))
378 eq(a//10, td(0, 7*24*360))
379 eq(a//3600000, td(0, 0, 7*24*1000))
380 eq(a/0.5, td(14))
381 eq(b/0.5, td(0, 120))
382 eq(a/7, td(1))
383 eq(b/10, td(0, 6))
384 eq(c/1000, td(0, 0, 1))
385 eq(a/10, td(0, 7*24*360))
386 eq(a/3600000, td(0, 0, 7*24*1000))
387
388 # Multiplication by float
389 us = td(microseconds=1)
390 eq((3*us) * 0.5, 2*us)
391 eq((5*us) * 0.5, 2*us)
392 eq(0.5 * (3*us), 2*us)
393 eq(0.5 * (5*us), 2*us)
394 eq((-3*us) * 0.5, -2*us)
395 eq((-5*us) * 0.5, -2*us)
396
397 # Division by int and float
398 eq((3*us) / 2, 2*us)
399 eq((5*us) / 2, 2*us)
400 eq((-3*us) / 2.0, -2*us)
401 eq((-5*us) / 2.0, -2*us)
402 eq((3*us) / -2, -2*us)
403 eq((5*us) / -2, -2*us)
404 eq((3*us) / -2.0, -2*us)
405 eq((5*us) / -2.0, -2*us)
406 for i in range(-10, 10):
407 eq((i*us/3)//us, round(i/3))
408 for i in range(-10, 10):
409 eq((i*us/-3)//us, round(i/-3))
410
Alexander Belopolskyb6f5ec72011-04-05 20:07:38 -0400411 # Issue #11576
412 eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
413 td(0, 0, 1))
414 eq(td(999999999, 1, 1) - td(999999999, 1, 0),
415 td(0, 0, 1))
416
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000417 def test_disallowed_computations(self):
418 a = timedelta(42)
419
420 # Add/sub ints or floats should be illegal
421 for i in 1, 1.0:
422 self.assertRaises(TypeError, lambda: a+i)
423 self.assertRaises(TypeError, lambda: a-i)
424 self.assertRaises(TypeError, lambda: i+a)
425 self.assertRaises(TypeError, lambda: i-a)
426
427 # Division of int by timedelta doesn't make sense.
428 # Division by zero doesn't make sense.
429 zero = 0
430 self.assertRaises(TypeError, lambda: zero // a)
431 self.assertRaises(ZeroDivisionError, lambda: a // zero)
432 self.assertRaises(ZeroDivisionError, lambda: a / zero)
433 self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
434 self.assertRaises(TypeError, lambda: a / '')
435
Eric Smith3ab08ca2010-12-04 15:17:38 +0000436 @support.requires_IEEE_754
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000437 def test_disallowed_special(self):
438 a = timedelta(42)
439 self.assertRaises(ValueError, a.__mul__, NAN)
440 self.assertRaises(ValueError, a.__truediv__, NAN)
441
442 def test_basic_attributes(self):
443 days, seconds, us = 1, 7, 31
444 td = timedelta(days, seconds, us)
445 self.assertEqual(td.days, days)
446 self.assertEqual(td.seconds, seconds)
447 self.assertEqual(td.microseconds, us)
448
449 def test_total_seconds(self):
450 td = timedelta(days=365)
451 self.assertEqual(td.total_seconds(), 31536000.0)
452 for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
453 td = timedelta(seconds=total_seconds)
454 self.assertEqual(td.total_seconds(), total_seconds)
455 # Issue8644: Test that td.total_seconds() has the same
456 # accuracy as td / timedelta(seconds=1).
457 for ms in [-1, -2, -123]:
458 td = timedelta(microseconds=ms)
459 self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
460
461 def test_carries(self):
462 t1 = timedelta(days=100,
463 weeks=-7,
464 hours=-24*(100-49),
465 minutes=-3,
466 seconds=12,
467 microseconds=(3*60 - 12) * 1e6 + 1)
468 t2 = timedelta(microseconds=1)
469 self.assertEqual(t1, t2)
470
471 def test_hash_equality(self):
472 t1 = timedelta(days=100,
473 weeks=-7,
474 hours=-24*(100-49),
475 minutes=-3,
476 seconds=12,
477 microseconds=(3*60 - 12) * 1000000)
478 t2 = timedelta()
479 self.assertEqual(hash(t1), hash(t2))
480
481 t1 += timedelta(weeks=7)
482 t2 += timedelta(days=7*7)
483 self.assertEqual(t1, t2)
484 self.assertEqual(hash(t1), hash(t2))
485
486 d = {t1: 1}
487 d[t2] = 2
488 self.assertEqual(len(d), 1)
489 self.assertEqual(d[t1], 2)
490
491 def test_pickling(self):
492 args = 12, 34, 56
493 orig = timedelta(*args)
494 for pickler, unpickler, proto in pickle_choices:
495 green = pickler.dumps(orig, proto)
496 derived = unpickler.loads(green)
497 self.assertEqual(orig, derived)
498
499 def test_compare(self):
500 t1 = timedelta(2, 3, 4)
501 t2 = timedelta(2, 3, 4)
502 self.assertEqual(t1, t2)
503 self.assertTrue(t1 <= t2)
504 self.assertTrue(t1 >= t2)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200505 self.assertFalse(t1 != t2)
506 self.assertFalse(t1 < t2)
507 self.assertFalse(t1 > t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000508
509 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
510 t2 = timedelta(*args) # this is larger than t1
511 self.assertTrue(t1 < t2)
512 self.assertTrue(t2 > t1)
513 self.assertTrue(t1 <= t2)
514 self.assertTrue(t2 >= t1)
515 self.assertTrue(t1 != t2)
516 self.assertTrue(t2 != t1)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200517 self.assertFalse(t1 == t2)
518 self.assertFalse(t2 == t1)
519 self.assertFalse(t1 > t2)
520 self.assertFalse(t2 < t1)
521 self.assertFalse(t1 >= t2)
522 self.assertFalse(t2 <= t1)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000523
524 for badarg in OTHERSTUFF:
525 self.assertEqual(t1 == badarg, False)
526 self.assertEqual(t1 != badarg, True)
527 self.assertEqual(badarg == t1, False)
528 self.assertEqual(badarg != t1, True)
529
530 self.assertRaises(TypeError, lambda: t1 <= badarg)
531 self.assertRaises(TypeError, lambda: t1 < badarg)
532 self.assertRaises(TypeError, lambda: t1 > badarg)
533 self.assertRaises(TypeError, lambda: t1 >= badarg)
534 self.assertRaises(TypeError, lambda: badarg <= t1)
535 self.assertRaises(TypeError, lambda: badarg < t1)
536 self.assertRaises(TypeError, lambda: badarg > t1)
537 self.assertRaises(TypeError, lambda: badarg >= t1)
538
539 def test_str(self):
540 td = timedelta
541 eq = self.assertEqual
542
543 eq(str(td(1)), "1 day, 0:00:00")
544 eq(str(td(-1)), "-1 day, 0:00:00")
545 eq(str(td(2)), "2 days, 0:00:00")
546 eq(str(td(-2)), "-2 days, 0:00:00")
547
548 eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
549 eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
550 eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
551 "-210 days, 23:12:34")
552
553 eq(str(td(milliseconds=1)), "0:00:00.001000")
554 eq(str(td(microseconds=3)), "0:00:00.000003")
555
556 eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
557 microseconds=999999)),
558 "999999999 days, 23:59:59.999999")
559
560 def test_repr(self):
561 name = 'datetime.' + self.theclass.__name__
562 self.assertEqual(repr(self.theclass(1)),
563 "%s(1)" % name)
564 self.assertEqual(repr(self.theclass(10, 2)),
565 "%s(10, 2)" % name)
566 self.assertEqual(repr(self.theclass(-10, 2, 400000)),
567 "%s(-10, 2, 400000)" % name)
568
569 def test_roundtrip(self):
570 for td in (timedelta(days=999999999, hours=23, minutes=59,
571 seconds=59, microseconds=999999),
572 timedelta(days=-999999999),
573 timedelta(days=-999999999, seconds=1),
574 timedelta(days=1, seconds=2, microseconds=3)):
575
576 # Verify td -> string -> td identity.
577 s = repr(td)
578 self.assertTrue(s.startswith('datetime.'))
579 s = s[9:]
580 td2 = eval(s)
581 self.assertEqual(td, td2)
582
583 # Verify identity via reconstructing from pieces.
584 td2 = timedelta(td.days, td.seconds, td.microseconds)
585 self.assertEqual(td, td2)
586
587 def test_resolution_info(self):
588 self.assertIsInstance(timedelta.min, timedelta)
589 self.assertIsInstance(timedelta.max, timedelta)
590 self.assertIsInstance(timedelta.resolution, timedelta)
591 self.assertTrue(timedelta.max > timedelta.min)
592 self.assertEqual(timedelta.min, timedelta(-999999999))
593 self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
594 self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
595
596 def test_overflow(self):
597 tiny = timedelta.resolution
598
599 td = timedelta.min + tiny
600 td -= tiny # no problem
601 self.assertRaises(OverflowError, td.__sub__, tiny)
602 self.assertRaises(OverflowError, td.__add__, -tiny)
603
604 td = timedelta.max - tiny
605 td += tiny # no problem
606 self.assertRaises(OverflowError, td.__add__, tiny)
607 self.assertRaises(OverflowError, td.__sub__, -tiny)
608
609 self.assertRaises(OverflowError, lambda: -timedelta.max)
610
611 day = timedelta(1)
612 self.assertRaises(OverflowError, day.__mul__, 10**9)
613 self.assertRaises(OverflowError, day.__mul__, 1e9)
614 self.assertRaises(OverflowError, day.__truediv__, 1e-20)
615 self.assertRaises(OverflowError, day.__truediv__, 1e-10)
616 self.assertRaises(OverflowError, day.__truediv__, 9e-10)
617
Eric Smith3ab08ca2010-12-04 15:17:38 +0000618 @support.requires_IEEE_754
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000619 def _test_overflow_special(self):
620 day = timedelta(1)
621 self.assertRaises(OverflowError, day.__mul__, INF)
622 self.assertRaises(OverflowError, day.__mul__, -INF)
623
624 def test_microsecond_rounding(self):
625 td = timedelta
626 eq = self.assertEqual
627
628 # Single-field rounding.
629 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
630 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -0400631 eq(td(milliseconds=0.5/1000), td(microseconds=0))
632 eq(td(milliseconds=-0.5/1000), td(microseconds=0))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000633 eq(td(milliseconds=0.6/1000), td(microseconds=1))
634 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -0400635 eq(td(seconds=0.5/10**6), td(microseconds=0))
636 eq(td(seconds=-0.5/10**6), td(microseconds=0))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000637
638 # Rounding due to contributions from more than one field.
639 us_per_hour = 3600e6
640 us_per_day = us_per_hour * 24
641 eq(td(days=.4/us_per_day), td(0))
642 eq(td(hours=.2/us_per_hour), td(0))
643 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
644
645 eq(td(days=-.4/us_per_day), td(0))
646 eq(td(hours=-.2/us_per_hour), td(0))
647 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
648
Alexander Belopolsky790d2692013-08-04 14:51:35 -0400649 # Test for a patch in Issue 8860
650 eq(td(microseconds=0.5), 0.5*td(microseconds=1.0))
651 eq(td(microseconds=0.5)//td.resolution, 0.5*td.resolution//td.resolution)
652
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000653 def test_massive_normalization(self):
654 td = timedelta(microseconds=-1)
655 self.assertEqual((td.days, td.seconds, td.microseconds),
656 (-1, 24*3600-1, 999999))
657
658 def test_bool(self):
659 self.assertTrue(timedelta(1))
660 self.assertTrue(timedelta(0, 1))
661 self.assertTrue(timedelta(0, 0, 1))
662 self.assertTrue(timedelta(microseconds=1))
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200663 self.assertFalse(timedelta(0))
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000664
665 def test_subclass_timedelta(self):
666
667 class T(timedelta):
668 @staticmethod
669 def from_td(td):
670 return T(td.days, td.seconds, td.microseconds)
671
672 def as_hours(self):
673 sum = (self.days * 24 +
674 self.seconds / 3600.0 +
675 self.microseconds / 3600e6)
676 return round(sum)
677
678 t1 = T(days=1)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200679 self.assertIs(type(t1), T)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000680 self.assertEqual(t1.as_hours(), 24)
681
682 t2 = T(days=-1, seconds=-3600)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200683 self.assertIs(type(t2), T)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000684 self.assertEqual(t2.as_hours(), -25)
685
686 t3 = t1 + t2
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200687 self.assertIs(type(t3), timedelta)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000688 t4 = T.from_td(t3)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +0200689 self.assertIs(type(t4), T)
Alexander Belopolskycf86e362010-07-23 19:25:47 +0000690 self.assertEqual(t3.days, t4.days)
691 self.assertEqual(t3.seconds, t4.seconds)
692 self.assertEqual(t3.microseconds, t4.microseconds)
693 self.assertEqual(str(t3), str(t4))
694 self.assertEqual(t4.as_hours(), -1)
695
696 def test_division(self):
697 t = timedelta(hours=1, minutes=24, seconds=19)
698 second = timedelta(seconds=1)
699 self.assertEqual(t / second, 5059.0)
700 self.assertEqual(t // second, 5059)
701
702 t = timedelta(minutes=2, seconds=30)
703 minute = timedelta(minutes=1)
704 self.assertEqual(t / minute, 2.5)
705 self.assertEqual(t // minute, 2)
706
707 zerotd = timedelta(0)
708 self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
709 self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
710
711 # self.assertRaises(TypeError, truediv, t, 2)
712 # note: floor division of a timedelta by an integer *is*
713 # currently permitted.
714
715 def test_remainder(self):
716 t = timedelta(minutes=2, seconds=30)
717 minute = timedelta(minutes=1)
718 r = t % minute
719 self.assertEqual(r, timedelta(seconds=30))
720
721 t = timedelta(minutes=-2, seconds=30)
722 r = t % minute
723 self.assertEqual(r, timedelta(seconds=30))
724
725 zerotd = timedelta(0)
726 self.assertRaises(ZeroDivisionError, mod, t, zerotd)
727
728 self.assertRaises(TypeError, mod, t, 10)
729
730 def test_divmod(self):
731 t = timedelta(minutes=2, seconds=30)
732 minute = timedelta(minutes=1)
733 q, r = divmod(t, minute)
734 self.assertEqual(q, 2)
735 self.assertEqual(r, timedelta(seconds=30))
736
737 t = timedelta(minutes=-2, seconds=30)
738 q, r = divmod(t, minute)
739 self.assertEqual(q, -2)
740 self.assertEqual(r, timedelta(seconds=30))
741
742 zerotd = timedelta(0)
743 self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
744
745 self.assertRaises(TypeError, divmod, t, 10)
746
747
748#############################################################################
749# date tests
750
751class TestDateOnly(unittest.TestCase):
752 # Tests here won't pass if also run on datetime objects, so don't
753 # subclass this to test datetimes too.
754
755 def test_delta_non_days_ignored(self):
756 dt = date(2000, 1, 2)
757 delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
758 microseconds=5)
759 days = timedelta(delta.days)
760 self.assertEqual(days, timedelta(1))
761
762 dt2 = dt + delta
763 self.assertEqual(dt2, dt + days)
764
765 dt2 = delta + dt
766 self.assertEqual(dt2, dt + days)
767
768 dt2 = dt - delta
769 self.assertEqual(dt2, dt - days)
770
771 delta = -delta
772 days = timedelta(delta.days)
773 self.assertEqual(days, timedelta(-2))
774
775 dt2 = dt + delta
776 self.assertEqual(dt2, dt + days)
777
778 dt2 = delta + dt
779 self.assertEqual(dt2, dt + days)
780
781 dt2 = dt - delta
782 self.assertEqual(dt2, dt - days)
783
784class SubclassDate(date):
785 sub_var = 1
786
787class TestDate(HarmlessMixedComparison, unittest.TestCase):
788 # Tests here should pass for both dates and datetimes, except for a
789 # few tests that TestDateTime overrides.
790
791 theclass = date
792
793 def test_basic_attributes(self):
794 dt = self.theclass(2002, 3, 1)
795 self.assertEqual(dt.year, 2002)
796 self.assertEqual(dt.month, 3)
797 self.assertEqual(dt.day, 1)
798
799 def test_roundtrip(self):
800 for dt in (self.theclass(1, 2, 3),
801 self.theclass.today()):
802 # Verify dt -> string -> date identity.
803 s = repr(dt)
804 self.assertTrue(s.startswith('datetime.'))
805 s = s[9:]
806 dt2 = eval(s)
807 self.assertEqual(dt, dt2)
808
809 # Verify identity via reconstructing from pieces.
810 dt2 = self.theclass(dt.year, dt.month, dt.day)
811 self.assertEqual(dt, dt2)
812
813 def test_ordinal_conversions(self):
814 # Check some fixed values.
815 for y, m, d, n in [(1, 1, 1, 1), # calendar origin
816 (1, 12, 31, 365),
817 (2, 1, 1, 366),
818 # first example from "Calendrical Calculations"
819 (1945, 11, 12, 710347)]:
820 d = self.theclass(y, m, d)
821 self.assertEqual(n, d.toordinal())
822 fromord = self.theclass.fromordinal(n)
823 self.assertEqual(d, fromord)
824 if hasattr(fromord, "hour"):
825 # if we're checking something fancier than a date, verify
826 # the extra fields have been zeroed out
827 self.assertEqual(fromord.hour, 0)
828 self.assertEqual(fromord.minute, 0)
829 self.assertEqual(fromord.second, 0)
830 self.assertEqual(fromord.microsecond, 0)
831
832 # Check first and last days of year spottily across the whole
833 # range of years supported.
834 for year in range(MINYEAR, MAXYEAR+1, 7):
835 # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
836 d = self.theclass(year, 1, 1)
837 n = d.toordinal()
838 d2 = self.theclass.fromordinal(n)
839 self.assertEqual(d, d2)
840 # Verify that moving back a day gets to the end of year-1.
841 if year > 1:
842 d = self.theclass.fromordinal(n-1)
843 d2 = self.theclass(year-1, 12, 31)
844 self.assertEqual(d, d2)
845 self.assertEqual(d2.toordinal(), n-1)
846
847 # Test every day in a leap-year and a non-leap year.
848 dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
849 for year, isleap in (2000, True), (2002, False):
850 n = self.theclass(year, 1, 1).toordinal()
851 for month, maxday in zip(range(1, 13), dim):
852 if month == 2 and isleap:
853 maxday += 1
854 for day in range(1, maxday+1):
855 d = self.theclass(year, month, day)
856 self.assertEqual(d.toordinal(), n)
857 self.assertEqual(d, self.theclass.fromordinal(n))
858 n += 1
859
860 def test_extreme_ordinals(self):
861 a = self.theclass.min
862 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
863 aord = a.toordinal()
864 b = a.fromordinal(aord)
865 self.assertEqual(a, b)
866
867 self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
868
869 b = a + timedelta(days=1)
870 self.assertEqual(b.toordinal(), aord + 1)
871 self.assertEqual(b, self.theclass.fromordinal(aord + 1))
872
873 a = self.theclass.max
874 a = self.theclass(a.year, a.month, a.day) # get rid of time parts
875 aord = a.toordinal()
876 b = a.fromordinal(aord)
877 self.assertEqual(a, b)
878
879 self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
880
881 b = a - timedelta(days=1)
882 self.assertEqual(b.toordinal(), aord - 1)
883 self.assertEqual(b, self.theclass.fromordinal(aord - 1))
884
885 def test_bad_constructor_arguments(self):
886 # bad years
887 self.theclass(MINYEAR, 1, 1) # no exception
888 self.theclass(MAXYEAR, 1, 1) # no exception
889 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
890 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
891 # bad months
892 self.theclass(2000, 1, 1) # no exception
893 self.theclass(2000, 12, 1) # no exception
894 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
895 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
896 # bad days
897 self.theclass(2000, 2, 29) # no exception
898 self.theclass(2004, 2, 29) # no exception
899 self.theclass(2400, 2, 29) # no exception
900 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
901 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
902 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
903 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
904 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
905 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
906
907 def test_hash_equality(self):
908 d = self.theclass(2000, 12, 31)
909 # same thing
910 e = self.theclass(2000, 12, 31)
911 self.assertEqual(d, e)
912 self.assertEqual(hash(d), hash(e))
913
914 dic = {d: 1}
915 dic[e] = 2
916 self.assertEqual(len(dic), 1)
917 self.assertEqual(dic[d], 2)
918 self.assertEqual(dic[e], 2)
919
920 d = self.theclass(2001, 1, 1)
921 # same thing
922 e = self.theclass(2001, 1, 1)
923 self.assertEqual(d, e)
924 self.assertEqual(hash(d), hash(e))
925
926 dic = {d: 1}
927 dic[e] = 2
928 self.assertEqual(len(dic), 1)
929 self.assertEqual(dic[d], 2)
930 self.assertEqual(dic[e], 2)
931
932 def test_computations(self):
933 a = self.theclass(2002, 1, 31)
934 b = self.theclass(1956, 1, 31)
935 c = self.theclass(2001,2,1)
936
937 diff = a-b
938 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
939 self.assertEqual(diff.seconds, 0)
940 self.assertEqual(diff.microseconds, 0)
941
942 day = timedelta(1)
943 week = timedelta(7)
944 a = self.theclass(2002, 3, 2)
945 self.assertEqual(a + day, self.theclass(2002, 3, 3))
946 self.assertEqual(day + a, self.theclass(2002, 3, 3))
947 self.assertEqual(a - day, self.theclass(2002, 3, 1))
948 self.assertEqual(-day + a, self.theclass(2002, 3, 1))
949 self.assertEqual(a + week, self.theclass(2002, 3, 9))
950 self.assertEqual(a - week, self.theclass(2002, 2, 23))
951 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
952 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
953 self.assertEqual((a + week) - a, week)
954 self.assertEqual((a + day) - a, day)
955 self.assertEqual((a - week) - a, -week)
956 self.assertEqual((a - day) - a, -day)
957 self.assertEqual(a - (a + week), -week)
958 self.assertEqual(a - (a + day), -day)
959 self.assertEqual(a - (a - week), week)
960 self.assertEqual(a - (a - day), day)
961 self.assertEqual(c - (c - day), day)
962
963 # Add/sub ints or floats should be illegal
964 for i in 1, 1.0:
965 self.assertRaises(TypeError, lambda: a+i)
966 self.assertRaises(TypeError, lambda: a-i)
967 self.assertRaises(TypeError, lambda: i+a)
968 self.assertRaises(TypeError, lambda: i-a)
969
970 # delta - date is senseless.
971 self.assertRaises(TypeError, lambda: day - a)
972 # mixing date and (delta or date) via * or // is senseless
973 self.assertRaises(TypeError, lambda: day * a)
974 self.assertRaises(TypeError, lambda: a * day)
975 self.assertRaises(TypeError, lambda: day // a)
976 self.assertRaises(TypeError, lambda: a // day)
977 self.assertRaises(TypeError, lambda: a * a)
978 self.assertRaises(TypeError, lambda: a // a)
979 # date + date is senseless
980 self.assertRaises(TypeError, lambda: a + a)
981
982 def test_overflow(self):
983 tiny = self.theclass.resolution
984
985 for delta in [tiny, timedelta(1), timedelta(2)]:
986 dt = self.theclass.min + delta
987 dt -= delta # no problem
988 self.assertRaises(OverflowError, dt.__sub__, delta)
989 self.assertRaises(OverflowError, dt.__add__, -delta)
990
991 dt = self.theclass.max - delta
992 dt += delta # no problem
993 self.assertRaises(OverflowError, dt.__add__, delta)
994 self.assertRaises(OverflowError, dt.__sub__, -delta)
995
996 def test_fromtimestamp(self):
997 import time
998
999 # Try an arbitrary fixed value.
1000 year, month, day = 1999, 9, 19
1001 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
1002 d = self.theclass.fromtimestamp(ts)
1003 self.assertEqual(d.year, year)
1004 self.assertEqual(d.month, month)
1005 self.assertEqual(d.day, day)
1006
1007 def test_insane_fromtimestamp(self):
1008 # It's possible that some platform maps time_t to double,
1009 # and that this test will fail there. This test should
1010 # exempt such platforms (provided they return reasonable
1011 # results!).
1012 for insane in -1e200, 1e200:
Victor Stinner5d272cc2012-03-13 13:35:55 +01001013 self.assertRaises(OverflowError, self.theclass.fromtimestamp,
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001014 insane)
1015
1016 def test_today(self):
1017 import time
1018
1019 # We claim that today() is like fromtimestamp(time.time()), so
1020 # prove it.
1021 for dummy in range(3):
1022 today = self.theclass.today()
1023 ts = time.time()
1024 todayagain = self.theclass.fromtimestamp(ts)
1025 if today == todayagain:
1026 break
1027 # There are several legit reasons that could fail:
1028 # 1. It recently became midnight, between the today() and the
1029 # time() calls.
1030 # 2. The platform time() has such fine resolution that we'll
1031 # never get the same value twice.
1032 # 3. The platform time() has poor resolution, and we just
1033 # happened to call today() right before a resolution quantum
1034 # boundary.
1035 # 4. The system clock got fiddled between calls.
1036 # In any case, wait a little while and try again.
1037 time.sleep(0.1)
1038
1039 # It worked or it didn't. If it didn't, assume it's reason #2, and
1040 # let the test pass if they're within half a second of each other.
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02001041 if today != todayagain:
1042 self.assertAlmostEqual(todayagain, today,
1043 delta=timedelta(seconds=0.5))
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001044
1045 def test_weekday(self):
1046 for i in range(7):
1047 # March 4, 2002 is a Monday
1048 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
1049 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
1050 # January 2, 1956 is a Monday
1051 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
1052 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
1053
1054 def test_isocalendar(self):
1055 # Check examples from
1056 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1057 for i in range(7):
1058 d = self.theclass(2003, 12, 22+i)
1059 self.assertEqual(d.isocalendar(), (2003, 52, i+1))
1060 d = self.theclass(2003, 12, 29) + timedelta(i)
1061 self.assertEqual(d.isocalendar(), (2004, 1, i+1))
1062 d = self.theclass(2004, 1, 5+i)
1063 self.assertEqual(d.isocalendar(), (2004, 2, i+1))
1064 d = self.theclass(2009, 12, 21+i)
1065 self.assertEqual(d.isocalendar(), (2009, 52, i+1))
1066 d = self.theclass(2009, 12, 28) + timedelta(i)
1067 self.assertEqual(d.isocalendar(), (2009, 53, i+1))
1068 d = self.theclass(2010, 1, 4+i)
1069 self.assertEqual(d.isocalendar(), (2010, 1, i+1))
1070
1071 def test_iso_long_years(self):
1072 # Calculate long ISO years and compare to table from
1073 # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1074 ISO_LONG_YEARS_TABLE = """
1075 4 32 60 88
1076 9 37 65 93
1077 15 43 71 99
1078 20 48 76
1079 26 54 82
1080
1081 105 133 161 189
1082 111 139 167 195
1083 116 144 172
1084 122 150 178
1085 128 156 184
1086
1087 201 229 257 285
1088 207 235 263 291
1089 212 240 268 296
1090 218 246 274
1091 224 252 280
1092
1093 303 331 359 387
1094 308 336 364 392
1095 314 342 370 398
1096 320 348 376
1097 325 353 381
1098 """
1099 iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
1100 L = []
1101 for i in range(400):
1102 d = self.theclass(2000+i, 12, 31)
1103 d1 = self.theclass(1600+i, 12, 31)
1104 self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
1105 if d.isocalendar()[1] == 53:
1106 L.append(i)
1107 self.assertEqual(L, iso_long_years)
1108
1109 def test_isoformat(self):
1110 t = self.theclass(2, 3, 2)
1111 self.assertEqual(t.isoformat(), "0002-03-02")
1112
1113 def test_ctime(self):
1114 t = self.theclass(2002, 3, 2)
1115 self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
1116
1117 def test_strftime(self):
1118 t = self.theclass(2005, 3, 2)
1119 self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
1120 self.assertEqual(t.strftime(""), "") # SF bug #761337
1121 self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
1122
1123 self.assertRaises(TypeError, t.strftime) # needs an arg
1124 self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
1125 self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
1126
1127 # test that unicode input is allowed (issue 2782)
1128 self.assertEqual(t.strftime("%m"), "03")
1129
1130 # A naive object replaces %z and %Z w/ empty strings.
1131 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1132
1133 #make sure that invalid format specifiers are handled correctly
1134 #self.assertRaises(ValueError, t.strftime, "%e")
1135 #self.assertRaises(ValueError, t.strftime, "%")
1136 #self.assertRaises(ValueError, t.strftime, "%#")
1137
1138 #oh well, some systems just ignore those invalid ones.
1139 #at least, excercise them to make sure that no crashes
1140 #are generated
1141 for f in ["%e", "%", "%#"]:
1142 try:
1143 t.strftime(f)
1144 except ValueError:
1145 pass
1146
1147 #check that this standard extension works
1148 t.strftime("%f")
1149
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001150 def test_format(self):
1151 dt = self.theclass(2007, 9, 10)
1152 self.assertEqual(dt.__format__(''), str(dt))
1153
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04001154 with self.assertRaisesRegex(TypeError, '^must be str, not int$'):
1155 dt.__format__(123)
1156
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001157 # check that a derived class's __str__() gets called
1158 class A(self.theclass):
1159 def __str__(self):
1160 return 'A'
1161 a = A(2007, 9, 10)
1162 self.assertEqual(a.__format__(''), 'A')
1163
1164 # check that a derived class's strftime gets called
1165 class B(self.theclass):
1166 def strftime(self, format_spec):
1167 return 'B'
1168 b = B(2007, 9, 10)
1169 self.assertEqual(b.__format__(''), str(dt))
1170
1171 for fmt in ["m:%m d:%d y:%y",
1172 "m:%m d:%d y:%y H:%H M:%M S:%S",
1173 "%z %Z",
1174 ]:
1175 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1176 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1177 self.assertEqual(b.__format__(fmt), 'B')
1178
1179 def test_resolution_info(self):
1180 # XXX: Should min and max respect subclassing?
1181 if issubclass(self.theclass, datetime):
1182 expected_class = datetime
1183 else:
1184 expected_class = date
1185 self.assertIsInstance(self.theclass.min, expected_class)
1186 self.assertIsInstance(self.theclass.max, expected_class)
1187 self.assertIsInstance(self.theclass.resolution, timedelta)
1188 self.assertTrue(self.theclass.max > self.theclass.min)
1189
1190 def test_extreme_timedelta(self):
1191 big = self.theclass.max - self.theclass.min
1192 # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
1193 n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
1194 # n == 315537897599999999 ~= 2**58.13
1195 justasbig = timedelta(0, 0, n)
1196 self.assertEqual(big, justasbig)
1197 self.assertEqual(self.theclass.min + big, self.theclass.max)
1198 self.assertEqual(self.theclass.max - big, self.theclass.min)
1199
1200 def test_timetuple(self):
1201 for i in range(7):
1202 # January 2, 1956 is a Monday (0)
1203 d = self.theclass(1956, 1, 2+i)
1204 t = d.timetuple()
1205 self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
1206 # February 1, 1956 is a Wednesday (2)
1207 d = self.theclass(1956, 2, 1+i)
1208 t = d.timetuple()
1209 self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
1210 # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
1211 # of the year.
1212 d = self.theclass(1956, 3, 1+i)
1213 t = d.timetuple()
1214 self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
1215 self.assertEqual(t.tm_year, 1956)
1216 self.assertEqual(t.tm_mon, 3)
1217 self.assertEqual(t.tm_mday, 1+i)
1218 self.assertEqual(t.tm_hour, 0)
1219 self.assertEqual(t.tm_min, 0)
1220 self.assertEqual(t.tm_sec, 0)
1221 self.assertEqual(t.tm_wday, (3+i)%7)
1222 self.assertEqual(t.tm_yday, 61+i)
1223 self.assertEqual(t.tm_isdst, -1)
1224
1225 def test_pickling(self):
1226 args = 6, 7, 23
1227 orig = self.theclass(*args)
1228 for pickler, unpickler, proto in pickle_choices:
1229 green = pickler.dumps(orig, proto)
1230 derived = unpickler.loads(green)
1231 self.assertEqual(orig, derived)
1232
1233 def test_compare(self):
1234 t1 = self.theclass(2, 3, 4)
1235 t2 = self.theclass(2, 3, 4)
1236 self.assertEqual(t1, t2)
1237 self.assertTrue(t1 <= t2)
1238 self.assertTrue(t1 >= t2)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02001239 self.assertFalse(t1 != t2)
1240 self.assertFalse(t1 < t2)
1241 self.assertFalse(t1 > t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001242
1243 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1244 t2 = self.theclass(*args) # this is larger than t1
1245 self.assertTrue(t1 < t2)
1246 self.assertTrue(t2 > t1)
1247 self.assertTrue(t1 <= t2)
1248 self.assertTrue(t2 >= t1)
1249 self.assertTrue(t1 != t2)
1250 self.assertTrue(t2 != t1)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02001251 self.assertFalse(t1 == t2)
1252 self.assertFalse(t2 == t1)
1253 self.assertFalse(t1 > t2)
1254 self.assertFalse(t2 < t1)
1255 self.assertFalse(t1 >= t2)
1256 self.assertFalse(t2 <= t1)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001257
1258 for badarg in OTHERSTUFF:
1259 self.assertEqual(t1 == badarg, False)
1260 self.assertEqual(t1 != badarg, True)
1261 self.assertEqual(badarg == t1, False)
1262 self.assertEqual(badarg != t1, True)
1263
1264 self.assertRaises(TypeError, lambda: t1 < badarg)
1265 self.assertRaises(TypeError, lambda: t1 > badarg)
1266 self.assertRaises(TypeError, lambda: t1 >= badarg)
1267 self.assertRaises(TypeError, lambda: badarg <= t1)
1268 self.assertRaises(TypeError, lambda: badarg < t1)
1269 self.assertRaises(TypeError, lambda: badarg > t1)
1270 self.assertRaises(TypeError, lambda: badarg >= t1)
1271
1272 def test_mixed_compare(self):
1273 our = self.theclass(2000, 4, 5)
1274
1275 # Our class can be compared for equality to other classes
1276 self.assertEqual(our == 1, False)
1277 self.assertEqual(1 == our, False)
1278 self.assertEqual(our != 1, True)
1279 self.assertEqual(1 != our, True)
1280
1281 # But the ordering is undefined
1282 self.assertRaises(TypeError, lambda: our < 1)
1283 self.assertRaises(TypeError, lambda: 1 < our)
1284
1285 # Repeat those tests with a different class
1286
1287 class SomeClass:
1288 pass
1289
1290 their = SomeClass()
1291 self.assertEqual(our == their, False)
1292 self.assertEqual(their == our, False)
1293 self.assertEqual(our != their, True)
1294 self.assertEqual(their != our, True)
1295 self.assertRaises(TypeError, lambda: our < their)
1296 self.assertRaises(TypeError, lambda: their < our)
1297
1298 # However, if the other class explicitly defines ordering
1299 # relative to our class, it is allowed to do so
1300
1301 class LargerThanAnything:
1302 def __lt__(self, other):
1303 return False
1304 def __le__(self, other):
1305 return isinstance(other, LargerThanAnything)
1306 def __eq__(self, other):
1307 return isinstance(other, LargerThanAnything)
1308 def __ne__(self, other):
1309 return not isinstance(other, LargerThanAnything)
1310 def __gt__(self, other):
1311 return not isinstance(other, LargerThanAnything)
1312 def __ge__(self, other):
1313 return True
1314
1315 their = LargerThanAnything()
1316 self.assertEqual(our == their, False)
1317 self.assertEqual(their == our, False)
1318 self.assertEqual(our != their, True)
1319 self.assertEqual(their != our, True)
1320 self.assertEqual(our < their, True)
1321 self.assertEqual(their < our, False)
1322
1323 def test_bool(self):
1324 # All dates are considered true.
1325 self.assertTrue(self.theclass.min)
1326 self.assertTrue(self.theclass.max)
1327
Alexander Belopolsky89da3492011-05-02 13:14:24 -04001328 def test_strftime_y2k(self):
1329 for y in (1, 49, 70, 99, 100, 999, 1000, 1970):
Florent Xicluna49ce0682011-11-01 12:56:14 +01001330 d = self.theclass(y, 1, 1)
1331 # Issue 13305: For years < 1000, the value is not always
1332 # padded to 4 digits across platforms. The C standard
1333 # assumes year >= 1900, so it does not specify the number
1334 # of digits.
1335 if d.strftime("%Y") != '%04d' % y:
1336 # Year 42 returns '42', not padded
1337 self.assertEqual(d.strftime("%Y"), '%d' % y)
1338 # '0042' is obtained anyway
1339 self.assertEqual(d.strftime("%4Y"), '%04d' % y)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001340
1341 def test_replace(self):
1342 cls = self.theclass
1343 args = [1, 2, 3]
1344 base = cls(*args)
1345 self.assertEqual(base, base.replace())
1346
1347 i = 0
1348 for name, newval in (("year", 2),
1349 ("month", 3),
1350 ("day", 4)):
1351 newargs = args[:]
1352 newargs[i] = newval
1353 expected = cls(*newargs)
1354 got = base.replace(**{name: newval})
1355 self.assertEqual(expected, got)
1356 i += 1
1357
1358 # Out of bounds.
1359 base = cls(2000, 2, 29)
1360 self.assertRaises(ValueError, base.replace, year=2001)
1361
1362 def test_subclass_date(self):
1363
1364 class C(self.theclass):
1365 theAnswer = 42
1366
1367 def __new__(cls, *args, **kws):
1368 temp = kws.copy()
1369 extra = temp.pop('extra')
1370 result = self.theclass.__new__(cls, *args, **temp)
1371 result.extra = extra
1372 return result
1373
1374 def newmeth(self, start):
1375 return start + self.year + self.month
1376
1377 args = 2003, 4, 14
1378
1379 dt1 = self.theclass(*args)
1380 dt2 = C(*args, **{'extra': 7})
1381
1382 self.assertEqual(dt2.__class__, C)
1383 self.assertEqual(dt2.theAnswer, 42)
1384 self.assertEqual(dt2.extra, 7)
1385 self.assertEqual(dt1.toordinal(), dt2.toordinal())
1386 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
1387
1388 def test_pickling_subclass_date(self):
1389
1390 args = 6, 7, 23
1391 orig = SubclassDate(*args)
1392 for pickler, unpickler, proto in pickle_choices:
1393 green = pickler.dumps(orig, proto)
1394 derived = unpickler.loads(green)
1395 self.assertEqual(orig, derived)
1396
1397 def test_backdoor_resistance(self):
1398 # For fast unpickling, the constructor accepts a pickle byte string.
1399 # This is a low-overhead backdoor. A user can (by intent or
1400 # mistake) pass a string directly, which (if it's the right length)
1401 # will get treated like a pickle, and bypass the normal sanity
1402 # checks in the constructor. This can create insane objects.
1403 # The constructor doesn't want to burn the time to validate all
1404 # fields, but does check the month field. This stops, e.g.,
1405 # datetime.datetime('1995-03-25') from yielding an insane object.
1406 base = b'1995-03-25'
1407 if not issubclass(self.theclass, datetime):
1408 base = base[:4]
1409 for month_byte in b'9', b'\0', b'\r', b'\xff':
1410 self.assertRaises(TypeError, self.theclass,
1411 base[:2] + month_byte + base[3:])
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04001412 if issubclass(self.theclass, datetime):
1413 # Good bytes, but bad tzinfo:
1414 with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
1415 self.theclass(bytes([1] * len(base)), 'EST')
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001416
1417 for ord_byte in range(1, 13):
1418 # This shouldn't blow up because of the month byte alone. If
1419 # the implementation changes to do more-careful checking, it may
1420 # blow up because other fields are insane.
1421 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
1422
1423#############################################################################
1424# datetime tests
1425
1426class SubclassDatetime(datetime):
1427 sub_var = 1
1428
1429class TestDateTime(TestDate):
1430
1431 theclass = datetime
1432
1433 def test_basic_attributes(self):
1434 dt = self.theclass(2002, 3, 1, 12, 0)
1435 self.assertEqual(dt.year, 2002)
1436 self.assertEqual(dt.month, 3)
1437 self.assertEqual(dt.day, 1)
1438 self.assertEqual(dt.hour, 12)
1439 self.assertEqual(dt.minute, 0)
1440 self.assertEqual(dt.second, 0)
1441 self.assertEqual(dt.microsecond, 0)
1442
1443 def test_basic_attributes_nonzero(self):
1444 # Make sure all attributes are non-zero so bugs in
1445 # bit-shifting access show up.
1446 dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
1447 self.assertEqual(dt.year, 2002)
1448 self.assertEqual(dt.month, 3)
1449 self.assertEqual(dt.day, 1)
1450 self.assertEqual(dt.hour, 12)
1451 self.assertEqual(dt.minute, 59)
1452 self.assertEqual(dt.second, 59)
1453 self.assertEqual(dt.microsecond, 8000)
1454
1455 def test_roundtrip(self):
1456 for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
1457 self.theclass.now()):
1458 # Verify dt -> string -> datetime identity.
1459 s = repr(dt)
1460 self.assertTrue(s.startswith('datetime.'))
1461 s = s[9:]
1462 dt2 = eval(s)
1463 self.assertEqual(dt, dt2)
1464
1465 # Verify identity via reconstructing from pieces.
1466 dt2 = self.theclass(dt.year, dt.month, dt.day,
1467 dt.hour, dt.minute, dt.second,
1468 dt.microsecond)
1469 self.assertEqual(dt, dt2)
1470
1471 def test_isoformat(self):
1472 t = self.theclass(2, 3, 2, 4, 5, 1, 123)
1473 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
1474 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
1475 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
1476 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
1477 # str is ISO format with the separator forced to a blank.
1478 self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
1479
1480 t = self.theclass(2, 3, 2)
1481 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1482 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1483 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1484 # str is ISO format with the separator forced to a blank.
1485 self.assertEqual(str(t), "0002-03-02 00:00:00")
1486
1487 def test_format(self):
1488 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1489 self.assertEqual(dt.__format__(''), str(dt))
1490
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04001491 with self.assertRaisesRegex(TypeError, '^must be str, not int$'):
1492 dt.__format__(123)
1493
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001494 # check that a derived class's __str__() gets called
1495 class A(self.theclass):
1496 def __str__(self):
1497 return 'A'
1498 a = A(2007, 9, 10, 4, 5, 1, 123)
1499 self.assertEqual(a.__format__(''), 'A')
1500
1501 # check that a derived class's strftime gets called
1502 class B(self.theclass):
1503 def strftime(self, format_spec):
1504 return 'B'
1505 b = B(2007, 9, 10, 4, 5, 1, 123)
1506 self.assertEqual(b.__format__(''), str(dt))
1507
1508 for fmt in ["m:%m d:%d y:%y",
1509 "m:%m d:%d y:%y H:%H M:%M S:%S",
1510 "%z %Z",
1511 ]:
1512 self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1513 self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1514 self.assertEqual(b.__format__(fmt), 'B')
1515
1516 def test_more_ctime(self):
1517 # Test fields that TestDate doesn't touch.
1518 import time
1519
1520 t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
1521 self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
1522 # Oops! The next line fails on Win2K under MSVC 6, so it's commented
1523 # out. The difference is that t.ctime() produces " 2" for the day,
1524 # but platform ctime() produces "02" for the day. According to
1525 # C99, t.ctime() is correct here.
1526 # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1527
1528 # So test a case where that difference doesn't matter.
1529 t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
1530 self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
1531
1532 def test_tz_independent_comparing(self):
1533 dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
1534 dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
1535 dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
1536 self.assertEqual(dt1, dt3)
1537 self.assertTrue(dt2 > dt3)
1538
1539 # Make sure comparison doesn't forget microseconds, and isn't done
1540 # via comparing a float timestamp (an IEEE double doesn't have enough
1541 # precision to span microsecond resolution across years 1 thru 9999,
1542 # so comparing via timestamp necessarily calls some distinct values
1543 # equal).
1544 dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
1545 us = timedelta(microseconds=1)
1546 dt2 = dt1 + us
1547 self.assertEqual(dt2 - dt1, us)
1548 self.assertTrue(dt1 < dt2)
1549
1550 def test_strftime_with_bad_tzname_replace(self):
1551 # verify ok if tzinfo.tzname().replace() returns a non-string
1552 class MyTzInfo(FixedOffset):
1553 def tzname(self, dt):
1554 class MyStr(str):
1555 def replace(self, *args):
1556 return None
1557 return MyStr('name')
1558 t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
1559 self.assertRaises(TypeError, t.strftime, '%Z')
1560
1561 def test_bad_constructor_arguments(self):
1562 # bad years
1563 self.theclass(MINYEAR, 1, 1) # no exception
1564 self.theclass(MAXYEAR, 1, 1) # no exception
1565 self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
1566 self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
1567 # bad months
1568 self.theclass(2000, 1, 1) # no exception
1569 self.theclass(2000, 12, 1) # no exception
1570 self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
1571 self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
1572 # bad days
1573 self.theclass(2000, 2, 29) # no exception
1574 self.theclass(2004, 2, 29) # no exception
1575 self.theclass(2400, 2, 29) # no exception
1576 self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
1577 self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
1578 self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
1579 self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
1580 self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
1581 self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
1582 # bad hours
1583 self.theclass(2000, 1, 31, 0) # no exception
1584 self.theclass(2000, 1, 31, 23) # no exception
1585 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
1586 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
1587 # bad minutes
1588 self.theclass(2000, 1, 31, 23, 0) # no exception
1589 self.theclass(2000, 1, 31, 23, 59) # no exception
1590 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
1591 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
1592 # bad seconds
1593 self.theclass(2000, 1, 31, 23, 59, 0) # no exception
1594 self.theclass(2000, 1, 31, 23, 59, 59) # no exception
1595 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1596 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1597 # bad microseconds
1598 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1599 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1600 self.assertRaises(ValueError, self.theclass,
1601 2000, 1, 31, 23, 59, 59, -1)
1602 self.assertRaises(ValueError, self.theclass,
1603 2000, 1, 31, 23, 59, 59,
1604 1000000)
1605
1606 def test_hash_equality(self):
1607 d = self.theclass(2000, 12, 31, 23, 30, 17)
1608 e = self.theclass(2000, 12, 31, 23, 30, 17)
1609 self.assertEqual(d, e)
1610 self.assertEqual(hash(d), hash(e))
1611
1612 dic = {d: 1}
1613 dic[e] = 2
1614 self.assertEqual(len(dic), 1)
1615 self.assertEqual(dic[d], 2)
1616 self.assertEqual(dic[e], 2)
1617
1618 d = self.theclass(2001, 1, 1, 0, 5, 17)
1619 e = self.theclass(2001, 1, 1, 0, 5, 17)
1620 self.assertEqual(d, e)
1621 self.assertEqual(hash(d), hash(e))
1622
1623 dic = {d: 1}
1624 dic[e] = 2
1625 self.assertEqual(len(dic), 1)
1626 self.assertEqual(dic[d], 2)
1627 self.assertEqual(dic[e], 2)
1628
1629 def test_computations(self):
1630 a = self.theclass(2002, 1, 31)
1631 b = self.theclass(1956, 1, 31)
1632 diff = a-b
1633 self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
1634 self.assertEqual(diff.seconds, 0)
1635 self.assertEqual(diff.microseconds, 0)
1636 a = self.theclass(2002, 3, 2, 17, 6)
1637 millisec = timedelta(0, 0, 1000)
1638 hour = timedelta(0, 3600)
1639 day = timedelta(1)
1640 week = timedelta(7)
1641 self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
1642 self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
1643 self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
1644 self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
1645 self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
1646 self.assertEqual(a - hour, a + -hour)
1647 self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
1648 self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
1649 self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
1650 self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
1651 self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
1652 self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
1653 self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
1654 self.assertEqual((a + week) - a, week)
1655 self.assertEqual((a + day) - a, day)
1656 self.assertEqual((a + hour) - a, hour)
1657 self.assertEqual((a + millisec) - a, millisec)
1658 self.assertEqual((a - week) - a, -week)
1659 self.assertEqual((a - day) - a, -day)
1660 self.assertEqual((a - hour) - a, -hour)
1661 self.assertEqual((a - millisec) - a, -millisec)
1662 self.assertEqual(a - (a + week), -week)
1663 self.assertEqual(a - (a + day), -day)
1664 self.assertEqual(a - (a + hour), -hour)
1665 self.assertEqual(a - (a + millisec), -millisec)
1666 self.assertEqual(a - (a - week), week)
1667 self.assertEqual(a - (a - day), day)
1668 self.assertEqual(a - (a - hour), hour)
1669 self.assertEqual(a - (a - millisec), millisec)
1670 self.assertEqual(a + (week + day + hour + millisec),
1671 self.theclass(2002, 3, 10, 18, 6, 0, 1000))
1672 self.assertEqual(a + (week + day + hour + millisec),
1673 (((a + week) + day) + hour) + millisec)
1674 self.assertEqual(a - (week + day + hour + millisec),
1675 self.theclass(2002, 2, 22, 16, 5, 59, 999000))
1676 self.assertEqual(a - (week + day + hour + millisec),
1677 (((a - week) - day) - hour) - millisec)
1678 # Add/sub ints or floats should be illegal
1679 for i in 1, 1.0:
1680 self.assertRaises(TypeError, lambda: a+i)
1681 self.assertRaises(TypeError, lambda: a-i)
1682 self.assertRaises(TypeError, lambda: i+a)
1683 self.assertRaises(TypeError, lambda: i-a)
1684
1685 # delta - datetime is senseless.
1686 self.assertRaises(TypeError, lambda: day - a)
1687 # mixing datetime and (delta or datetime) via * or // is senseless
1688 self.assertRaises(TypeError, lambda: day * a)
1689 self.assertRaises(TypeError, lambda: a * day)
1690 self.assertRaises(TypeError, lambda: day // a)
1691 self.assertRaises(TypeError, lambda: a // day)
1692 self.assertRaises(TypeError, lambda: a * a)
1693 self.assertRaises(TypeError, lambda: a // a)
1694 # datetime + datetime is senseless
1695 self.assertRaises(TypeError, lambda: a + a)
1696
1697 def test_pickling(self):
1698 args = 6, 7, 23, 20, 59, 1, 64**2
1699 orig = self.theclass(*args)
1700 for pickler, unpickler, proto in pickle_choices:
1701 green = pickler.dumps(orig, proto)
1702 derived = unpickler.loads(green)
1703 self.assertEqual(orig, derived)
1704
1705 def test_more_pickling(self):
1706 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001707 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1708 s = pickle.dumps(a, proto)
1709 b = pickle.loads(s)
1710 self.assertEqual(b.year, 2003)
1711 self.assertEqual(b.month, 2)
1712 self.assertEqual(b.day, 7)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001713
1714 def test_pickling_subclass_datetime(self):
1715 args = 6, 7, 23, 20, 59, 1, 64**2
1716 orig = SubclassDatetime(*args)
1717 for pickler, unpickler, proto in pickle_choices:
1718 green = pickler.dumps(orig, proto)
1719 derived = unpickler.loads(green)
1720 self.assertEqual(orig, derived)
1721
1722 def test_more_compare(self):
1723 # The test_compare() inherited from TestDate covers the error cases.
1724 # We just want to test lexicographic ordering on the members datetime
1725 # has that date lacks.
1726 args = [2000, 11, 29, 20, 58, 16, 999998]
1727 t1 = self.theclass(*args)
1728 t2 = self.theclass(*args)
1729 self.assertEqual(t1, t2)
1730 self.assertTrue(t1 <= t2)
1731 self.assertTrue(t1 >= t2)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02001732 self.assertFalse(t1 != t2)
1733 self.assertFalse(t1 < t2)
1734 self.assertFalse(t1 > t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001735
1736 for i in range(len(args)):
1737 newargs = args[:]
1738 newargs[i] = args[i] + 1
1739 t2 = self.theclass(*newargs) # this is larger than t1
1740 self.assertTrue(t1 < t2)
1741 self.assertTrue(t2 > t1)
1742 self.assertTrue(t1 <= t2)
1743 self.assertTrue(t2 >= t1)
1744 self.assertTrue(t1 != t2)
1745 self.assertTrue(t2 != t1)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02001746 self.assertFalse(t1 == t2)
1747 self.assertFalse(t2 == t1)
1748 self.assertFalse(t1 > t2)
1749 self.assertFalse(t2 < t1)
1750 self.assertFalse(t1 >= t2)
1751 self.assertFalse(t2 <= t1)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001752
1753
1754 # A helper for timestamp constructor tests.
1755 def verify_field_equality(self, expected, got):
1756 self.assertEqual(expected.tm_year, got.year)
1757 self.assertEqual(expected.tm_mon, got.month)
1758 self.assertEqual(expected.tm_mday, got.day)
1759 self.assertEqual(expected.tm_hour, got.hour)
1760 self.assertEqual(expected.tm_min, got.minute)
1761 self.assertEqual(expected.tm_sec, got.second)
1762
1763 def test_fromtimestamp(self):
1764 import time
1765
1766 ts = time.time()
1767 expected = time.localtime(ts)
1768 got = self.theclass.fromtimestamp(ts)
1769 self.verify_field_equality(expected, got)
1770
1771 def test_utcfromtimestamp(self):
1772 import time
1773
1774 ts = time.time()
1775 expected = time.gmtime(ts)
1776 got = self.theclass.utcfromtimestamp(ts)
1777 self.verify_field_equality(expected, got)
1778
Alexander Belopolskya4415142012-06-08 12:33:09 -04001779 # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
1780 # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
1781 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
1782 def test_timestamp_naive(self):
1783 t = self.theclass(1970, 1, 1)
1784 self.assertEqual(t.timestamp(), 18000.0)
1785 t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
1786 self.assertEqual(t.timestamp(),
1787 18000.0 + 3600 + 2*60 + 3 + 4*1e-6)
Alexander Belopolsky0c687e52012-06-08 12:58:31 -04001788 # Missing hour may produce platform-dependent result
Alexander Belopolskya4415142012-06-08 12:33:09 -04001789 t = self.theclass(2012, 3, 11, 2, 30)
Alexander Belopolsky0c687e52012-06-08 12:58:31 -04001790 self.assertIn(self.theclass.fromtimestamp(t.timestamp()),
Alexander Belopolskyf6f56182012-06-08 13:00:27 -04001791 [t - timedelta(hours=1), t + timedelta(hours=1)])
Alexander Belopolskya4415142012-06-08 12:33:09 -04001792 # Ambiguous hour defaults to DST
1793 t = self.theclass(2012, 11, 4, 1, 30)
1794 self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)
1795
1796 # Timestamp may raise an overflow error on some platforms
1797 for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]:
1798 try:
1799 s = t.timestamp()
1800 except OverflowError:
1801 pass
1802 else:
1803 self.assertEqual(self.theclass.fromtimestamp(s), t)
1804
1805 def test_timestamp_aware(self):
1806 t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)
1807 self.assertEqual(t.timestamp(), 0.0)
1808 t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)
1809 self.assertEqual(t.timestamp(),
1810 3600 + 2*60 + 3 + 4*1e-6)
1811 t = self.theclass(1970, 1, 1, 1, 2, 3, 4,
1812 tzinfo=timezone(timedelta(hours=-5), 'EST'))
1813 self.assertEqual(t.timestamp(),
1814 18000 + 3600 + 2*60 + 3 + 4*1e-6)
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04001815
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001816 def test_microsecond_rounding(self):
Alexander Belopolsky3e62f782010-09-21 16:30:56 +00001817 for fts in [self.theclass.fromtimestamp,
1818 self.theclass.utcfromtimestamp]:
Victor Stinner5d272cc2012-03-13 13:35:55 +01001819 zero = fts(0)
1820 self.assertEqual(zero.second, 0)
1821 self.assertEqual(zero.microsecond, 0)
Victor Stinner8050ca92012-03-14 00:17:05 +01001822 try:
1823 minus_one = fts(-1e-6)
1824 except OSError:
1825 # localtime(-1) and gmtime(-1) is not supported on Windows
1826 pass
1827 else:
1828 self.assertEqual(minus_one.second, 59)
1829 self.assertEqual(minus_one.microsecond, 999999)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001830
Victor Stinner8050ca92012-03-14 00:17:05 +01001831 t = fts(-1e-8)
1832 self.assertEqual(t, minus_one)
1833 t = fts(-9e-7)
1834 self.assertEqual(t, minus_one)
1835 t = fts(-1e-7)
1836 self.assertEqual(t, minus_one)
Victor Stinner5d272cc2012-03-13 13:35:55 +01001837
1838 t = fts(1e-7)
1839 self.assertEqual(t, zero)
1840 t = fts(9e-7)
1841 self.assertEqual(t, zero)
1842 t = fts(0.99999949)
1843 self.assertEqual(t.second, 0)
1844 self.assertEqual(t.microsecond, 999999)
1845 t = fts(0.9999999)
1846 self.assertEqual(t.second, 0)
1847 self.assertEqual(t.microsecond, 999999)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001848
1849 def test_insane_fromtimestamp(self):
1850 # It's possible that some platform maps time_t to double,
1851 # and that this test will fail there. This test should
1852 # exempt such platforms (provided they return reasonable
1853 # results!).
1854 for insane in -1e200, 1e200:
Victor Stinner5d272cc2012-03-13 13:35:55 +01001855 self.assertRaises(OverflowError, self.theclass.fromtimestamp,
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001856 insane)
1857
1858 def test_insane_utcfromtimestamp(self):
1859 # It's possible that some platform maps time_t to double,
1860 # and that this test will fail there. This test should
1861 # exempt such platforms (provided they return reasonable
1862 # results!).
1863 for insane in -1e200, 1e200:
Victor Stinner5d272cc2012-03-13 13:35:55 +01001864 self.assertRaises(OverflowError, self.theclass.utcfromtimestamp,
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001865 insane)
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04001866
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001867 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
1868 def test_negative_float_fromtimestamp(self):
1869 # The result is tz-dependent; at least test that this doesn't
1870 # fail (like it did before bug 1646728 was fixed).
1871 self.theclass.fromtimestamp(-1.05)
1872
1873 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
1874 def test_negative_float_utcfromtimestamp(self):
1875 d = self.theclass.utcfromtimestamp(-1.05)
1876 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1877
1878 def test_utcnow(self):
1879 import time
1880
1881 # Call it a success if utcnow() and utcfromtimestamp() are within
1882 # a second of each other.
1883 tolerance = timedelta(seconds=1)
1884 for dummy in range(3):
1885 from_now = self.theclass.utcnow()
1886 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1887 if abs(from_timestamp - from_now) <= tolerance:
1888 break
1889 # Else try again a few times.
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02001890 self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001891
1892 def test_strptime(self):
Alexander Belopolskycf86e362010-07-23 19:25:47 +00001893 string = '2004-12-01 13:02:47.197'
1894 format = '%Y-%m-%d %H:%M:%S.%f'
1895 expected = _strptime._strptime_datetime(self.theclass, string, format)
1896 got = self.theclass.strptime(string, format)
1897 self.assertEqual(expected, got)
1898 self.assertIs(type(expected), self.theclass)
1899 self.assertIs(type(got), self.theclass)
1900
1901 strptime = self.theclass.strptime
1902 self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
1903 self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
1904 # Only local timezone and UTC are supported
1905 for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
1906 (-_time.timezone, _time.tzname[0])):
1907 if tzseconds < 0:
1908 sign = '-'
1909 seconds = -tzseconds
1910 else:
1911 sign ='+'
1912 seconds = tzseconds
1913 hours, minutes = divmod(seconds//60, 60)
1914 dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
1915 dt = strptime(dtstr, "%z %Z")
1916 self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
1917 self.assertEqual(dt.tzname(), tzname)
1918 # Can produce inconsistent datetime
1919 dtstr, fmt = "+1234 UTC", "%z %Z"
1920 dt = strptime(dtstr, fmt)
1921 self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
1922 self.assertEqual(dt.tzname(), 'UTC')
1923 # yet will roundtrip
1924 self.assertEqual(dt.strftime(fmt), dtstr)
1925
1926 # Produce naive datetime if no %z is provided
1927 self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
1928
1929 with self.assertRaises(ValueError): strptime("-2400", "%z")
1930 with self.assertRaises(ValueError): strptime("-000", "%z")
1931
1932 def test_more_timetuple(self):
1933 # This tests fields beyond those tested by the TestDate.test_timetuple.
1934 t = self.theclass(2004, 12, 31, 6, 22, 33)
1935 self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
1936 self.assertEqual(t.timetuple(),
1937 (t.year, t.month, t.day,
1938 t.hour, t.minute, t.second,
1939 t.weekday(),
1940 t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
1941 -1))
1942 tt = t.timetuple()
1943 self.assertEqual(tt.tm_year, t.year)
1944 self.assertEqual(tt.tm_mon, t.month)
1945 self.assertEqual(tt.tm_mday, t.day)
1946 self.assertEqual(tt.tm_hour, t.hour)
1947 self.assertEqual(tt.tm_min, t.minute)
1948 self.assertEqual(tt.tm_sec, t.second)
1949 self.assertEqual(tt.tm_wday, t.weekday())
1950 self.assertEqual(tt.tm_yday, t.toordinal() -
1951 date(t.year, 1, 1).toordinal() + 1)
1952 self.assertEqual(tt.tm_isdst, -1)
1953
1954 def test_more_strftime(self):
1955 # This tests fields beyond those tested by the TestDate.test_strftime.
1956 t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
1957 self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
1958 "12 31 04 000047 33 22 06 366")
1959
1960 def test_extract(self):
1961 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1962 self.assertEqual(dt.date(), date(2002, 3, 4))
1963 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
1964
1965 def test_combine(self):
1966 d = date(2002, 3, 4)
1967 t = time(18, 45, 3, 1234)
1968 expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
1969 combine = self.theclass.combine
1970 dt = combine(d, t)
1971 self.assertEqual(dt, expected)
1972
1973 dt = combine(time=t, date=d)
1974 self.assertEqual(dt, expected)
1975
1976 self.assertEqual(d, dt.date())
1977 self.assertEqual(t, dt.time())
1978 self.assertEqual(dt, combine(dt.date(), dt.time()))
1979
1980 self.assertRaises(TypeError, combine) # need an arg
1981 self.assertRaises(TypeError, combine, d) # need two args
1982 self.assertRaises(TypeError, combine, t, d) # args reversed
1983 self.assertRaises(TypeError, combine, d, t, 1) # too many args
1984 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1985 self.assertRaises(TypeError, combine, d, "time") # wrong type
1986 self.assertRaises(TypeError, combine, "date", t) # wrong type
1987
1988 def test_replace(self):
1989 cls = self.theclass
1990 args = [1, 2, 3, 4, 5, 6, 7]
1991 base = cls(*args)
1992 self.assertEqual(base, base.replace())
1993
1994 i = 0
1995 for name, newval in (("year", 2),
1996 ("month", 3),
1997 ("day", 4),
1998 ("hour", 5),
1999 ("minute", 6),
2000 ("second", 7),
2001 ("microsecond", 8)):
2002 newargs = args[:]
2003 newargs[i] = newval
2004 expected = cls(*newargs)
2005 got = base.replace(**{name: newval})
2006 self.assertEqual(expected, got)
2007 i += 1
2008
2009 # Out of bounds.
2010 base = cls(2000, 2, 29)
2011 self.assertRaises(ValueError, base.replace, year=2001)
2012
2013 def test_astimezone(self):
2014 # Pretty boring! The TZ test is more interesting here. astimezone()
2015 # simply can't be applied to a naive object.
2016 dt = self.theclass.now()
2017 f = FixedOffset(44, "")
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04002018 self.assertRaises(ValueError, dt.astimezone) # naive
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002019 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
2020 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
2021 self.assertRaises(ValueError, dt.astimezone, f) # naive
2022 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
2023
2024 class Bogus(tzinfo):
2025 def utcoffset(self, dt): return None
2026 def dst(self, dt): return timedelta(0)
2027 bog = Bogus()
2028 self.assertRaises(ValueError, dt.astimezone, bog) # naive
2029 self.assertRaises(ValueError,
2030 dt.replace(tzinfo=bog).astimezone, f)
2031
2032 class AlsoBogus(tzinfo):
2033 def utcoffset(self, dt): return timedelta(0)
2034 def dst(self, dt): return None
2035 alsobog = AlsoBogus()
2036 self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
2037
2038 def test_subclass_datetime(self):
2039
2040 class C(self.theclass):
2041 theAnswer = 42
2042
2043 def __new__(cls, *args, **kws):
2044 temp = kws.copy()
2045 extra = temp.pop('extra')
2046 result = self.theclass.__new__(cls, *args, **temp)
2047 result.extra = extra
2048 return result
2049
2050 def newmeth(self, start):
2051 return start + self.year + self.month + self.second
2052
2053 args = 2003, 4, 14, 12, 13, 41
2054
2055 dt1 = self.theclass(*args)
2056 dt2 = C(*args, **{'extra': 7})
2057
2058 self.assertEqual(dt2.__class__, C)
2059 self.assertEqual(dt2.theAnswer, 42)
2060 self.assertEqual(dt2.extra, 7)
2061 self.assertEqual(dt1.toordinal(), dt2.toordinal())
2062 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
2063 dt1.second - 7)
2064
2065class TestSubclassDateTime(TestDateTime):
2066 theclass = SubclassDatetime
2067 # Override tests not designed for subclass
Zachary Ware9fe6d862013-12-08 00:20:35 -06002068 @unittest.skip('not appropriate for subclasses')
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002069 def test_roundtrip(self):
2070 pass
2071
2072class SubclassTime(time):
2073 sub_var = 1
2074
2075class TestTime(HarmlessMixedComparison, unittest.TestCase):
2076
2077 theclass = time
2078
2079 def test_basic_attributes(self):
2080 t = self.theclass(12, 0)
2081 self.assertEqual(t.hour, 12)
2082 self.assertEqual(t.minute, 0)
2083 self.assertEqual(t.second, 0)
2084 self.assertEqual(t.microsecond, 0)
2085
2086 def test_basic_attributes_nonzero(self):
2087 # Make sure all attributes are non-zero so bugs in
2088 # bit-shifting access show up.
2089 t = self.theclass(12, 59, 59, 8000)
2090 self.assertEqual(t.hour, 12)
2091 self.assertEqual(t.minute, 59)
2092 self.assertEqual(t.second, 59)
2093 self.assertEqual(t.microsecond, 8000)
2094
2095 def test_roundtrip(self):
2096 t = self.theclass(1, 2, 3, 4)
2097
2098 # Verify t -> string -> time identity.
2099 s = repr(t)
2100 self.assertTrue(s.startswith('datetime.'))
2101 s = s[9:]
2102 t2 = eval(s)
2103 self.assertEqual(t, t2)
2104
2105 # Verify identity via reconstructing from pieces.
2106 t2 = self.theclass(t.hour, t.minute, t.second,
2107 t.microsecond)
2108 self.assertEqual(t, t2)
2109
2110 def test_comparing(self):
2111 args = [1, 2, 3, 4]
2112 t1 = self.theclass(*args)
2113 t2 = self.theclass(*args)
2114 self.assertEqual(t1, t2)
2115 self.assertTrue(t1 <= t2)
2116 self.assertTrue(t1 >= t2)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002117 self.assertFalse(t1 != t2)
2118 self.assertFalse(t1 < t2)
2119 self.assertFalse(t1 > t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002120
2121 for i in range(len(args)):
2122 newargs = args[:]
2123 newargs[i] = args[i] + 1
2124 t2 = self.theclass(*newargs) # this is larger than t1
2125 self.assertTrue(t1 < t2)
2126 self.assertTrue(t2 > t1)
2127 self.assertTrue(t1 <= t2)
2128 self.assertTrue(t2 >= t1)
2129 self.assertTrue(t1 != t2)
2130 self.assertTrue(t2 != t1)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002131 self.assertFalse(t1 == t2)
2132 self.assertFalse(t2 == t1)
2133 self.assertFalse(t1 > t2)
2134 self.assertFalse(t2 < t1)
2135 self.assertFalse(t1 >= t2)
2136 self.assertFalse(t2 <= t1)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002137
2138 for badarg in OTHERSTUFF:
2139 self.assertEqual(t1 == badarg, False)
2140 self.assertEqual(t1 != badarg, True)
2141 self.assertEqual(badarg == t1, False)
2142 self.assertEqual(badarg != t1, True)
2143
2144 self.assertRaises(TypeError, lambda: t1 <= badarg)
2145 self.assertRaises(TypeError, lambda: t1 < badarg)
2146 self.assertRaises(TypeError, lambda: t1 > badarg)
2147 self.assertRaises(TypeError, lambda: t1 >= badarg)
2148 self.assertRaises(TypeError, lambda: badarg <= t1)
2149 self.assertRaises(TypeError, lambda: badarg < t1)
2150 self.assertRaises(TypeError, lambda: badarg > t1)
2151 self.assertRaises(TypeError, lambda: badarg >= t1)
2152
2153 def test_bad_constructor_arguments(self):
2154 # bad hours
2155 self.theclass(0, 0) # no exception
2156 self.theclass(23, 0) # no exception
2157 self.assertRaises(ValueError, self.theclass, -1, 0)
2158 self.assertRaises(ValueError, self.theclass, 24, 0)
2159 # bad minutes
2160 self.theclass(23, 0) # no exception
2161 self.theclass(23, 59) # no exception
2162 self.assertRaises(ValueError, self.theclass, 23, -1)
2163 self.assertRaises(ValueError, self.theclass, 23, 60)
2164 # bad seconds
2165 self.theclass(23, 59, 0) # no exception
2166 self.theclass(23, 59, 59) # no exception
2167 self.assertRaises(ValueError, self.theclass, 23, 59, -1)
2168 self.assertRaises(ValueError, self.theclass, 23, 59, 60)
2169 # bad microseconds
2170 self.theclass(23, 59, 59, 0) # no exception
2171 self.theclass(23, 59, 59, 999999) # no exception
2172 self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
2173 self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
2174
2175 def test_hash_equality(self):
2176 d = self.theclass(23, 30, 17)
2177 e = self.theclass(23, 30, 17)
2178 self.assertEqual(d, e)
2179 self.assertEqual(hash(d), hash(e))
2180
2181 dic = {d: 1}
2182 dic[e] = 2
2183 self.assertEqual(len(dic), 1)
2184 self.assertEqual(dic[d], 2)
2185 self.assertEqual(dic[e], 2)
2186
2187 d = self.theclass(0, 5, 17)
2188 e = self.theclass(0, 5, 17)
2189 self.assertEqual(d, e)
2190 self.assertEqual(hash(d), hash(e))
2191
2192 dic = {d: 1}
2193 dic[e] = 2
2194 self.assertEqual(len(dic), 1)
2195 self.assertEqual(dic[d], 2)
2196 self.assertEqual(dic[e], 2)
2197
2198 def test_isoformat(self):
2199 t = self.theclass(4, 5, 1, 123)
2200 self.assertEqual(t.isoformat(), "04:05:01.000123")
2201 self.assertEqual(t.isoformat(), str(t))
2202
2203 t = self.theclass()
2204 self.assertEqual(t.isoformat(), "00:00:00")
2205 self.assertEqual(t.isoformat(), str(t))
2206
2207 t = self.theclass(microsecond=1)
2208 self.assertEqual(t.isoformat(), "00:00:00.000001")
2209 self.assertEqual(t.isoformat(), str(t))
2210
2211 t = self.theclass(microsecond=10)
2212 self.assertEqual(t.isoformat(), "00:00:00.000010")
2213 self.assertEqual(t.isoformat(), str(t))
2214
2215 t = self.theclass(microsecond=100)
2216 self.assertEqual(t.isoformat(), "00:00:00.000100")
2217 self.assertEqual(t.isoformat(), str(t))
2218
2219 t = self.theclass(microsecond=1000)
2220 self.assertEqual(t.isoformat(), "00:00:00.001000")
2221 self.assertEqual(t.isoformat(), str(t))
2222
2223 t = self.theclass(microsecond=10000)
2224 self.assertEqual(t.isoformat(), "00:00:00.010000")
2225 self.assertEqual(t.isoformat(), str(t))
2226
2227 t = self.theclass(microsecond=100000)
2228 self.assertEqual(t.isoformat(), "00:00:00.100000")
2229 self.assertEqual(t.isoformat(), str(t))
2230
2231 def test_1653736(self):
2232 # verify it doesn't accept extra keyword arguments
2233 t = self.theclass(second=1)
2234 self.assertRaises(TypeError, t.isoformat, foo=3)
2235
2236 def test_strftime(self):
2237 t = self.theclass(1, 2, 3, 4)
2238 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
2239 # A naive object replaces %z and %Z with empty strings.
2240 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2241
2242 def test_format(self):
2243 t = self.theclass(1, 2, 3, 4)
2244 self.assertEqual(t.__format__(''), str(t))
2245
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04002246 with self.assertRaisesRegex(TypeError, '^must be str, not int$'):
2247 t.__format__(123)
2248
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002249 # check that a derived class's __str__() gets called
2250 class A(self.theclass):
2251 def __str__(self):
2252 return 'A'
2253 a = A(1, 2, 3, 4)
2254 self.assertEqual(a.__format__(''), 'A')
2255
2256 # check that a derived class's strftime gets called
2257 class B(self.theclass):
2258 def strftime(self, format_spec):
2259 return 'B'
2260 b = B(1, 2, 3, 4)
2261 self.assertEqual(b.__format__(''), str(t))
2262
2263 for fmt in ['%H %M %S',
2264 ]:
2265 self.assertEqual(t.__format__(fmt), t.strftime(fmt))
2266 self.assertEqual(a.__format__(fmt), t.strftime(fmt))
2267 self.assertEqual(b.__format__(fmt), 'B')
2268
2269 def test_str(self):
2270 self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
2271 self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
2272 self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
2273 self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
2274 self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
2275
2276 def test_repr(self):
2277 name = 'datetime.' + self.theclass.__name__
2278 self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
2279 "%s(1, 2, 3, 4)" % name)
2280 self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
2281 "%s(10, 2, 3, 4000)" % name)
2282 self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
2283 "%s(0, 2, 3, 400000)" % name)
2284 self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
2285 "%s(12, 2, 3)" % name)
2286 self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
2287 "%s(23, 15)" % name)
2288
2289 def test_resolution_info(self):
2290 self.assertIsInstance(self.theclass.min, self.theclass)
2291 self.assertIsInstance(self.theclass.max, self.theclass)
2292 self.assertIsInstance(self.theclass.resolution, timedelta)
2293 self.assertTrue(self.theclass.max > self.theclass.min)
2294
2295 def test_pickling(self):
2296 args = 20, 59, 16, 64**2
2297 orig = self.theclass(*args)
2298 for pickler, unpickler, proto in pickle_choices:
2299 green = pickler.dumps(orig, proto)
2300 derived = unpickler.loads(green)
2301 self.assertEqual(orig, derived)
2302
2303 def test_pickling_subclass_time(self):
2304 args = 20, 59, 16, 64**2
2305 orig = SubclassTime(*args)
2306 for pickler, unpickler, proto in pickle_choices:
2307 green = pickler.dumps(orig, proto)
2308 derived = unpickler.loads(green)
2309 self.assertEqual(orig, derived)
2310
2311 def test_bool(self):
Benjamin Petersonee6bdc02014-03-20 18:00:35 -05002312 # time is always True.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002313 cls = self.theclass
2314 self.assertTrue(cls(1))
2315 self.assertTrue(cls(0, 1))
2316 self.assertTrue(cls(0, 0, 1))
2317 self.assertTrue(cls(0, 0, 0, 1))
Benjamin Petersonee6bdc02014-03-20 18:00:35 -05002318 self.assertTrue(cls(0))
2319 self.assertTrue(cls())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002320
2321 def test_replace(self):
2322 cls = self.theclass
2323 args = [1, 2, 3, 4]
2324 base = cls(*args)
2325 self.assertEqual(base, base.replace())
2326
2327 i = 0
2328 for name, newval in (("hour", 5),
2329 ("minute", 6),
2330 ("second", 7),
2331 ("microsecond", 8)):
2332 newargs = args[:]
2333 newargs[i] = newval
2334 expected = cls(*newargs)
2335 got = base.replace(**{name: newval})
2336 self.assertEqual(expected, got)
2337 i += 1
2338
2339 # Out of bounds.
2340 base = cls(1)
2341 self.assertRaises(ValueError, base.replace, hour=24)
2342 self.assertRaises(ValueError, base.replace, minute=-1)
2343 self.assertRaises(ValueError, base.replace, second=100)
2344 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2345
2346 def test_subclass_time(self):
2347
2348 class C(self.theclass):
2349 theAnswer = 42
2350
2351 def __new__(cls, *args, **kws):
2352 temp = kws.copy()
2353 extra = temp.pop('extra')
2354 result = self.theclass.__new__(cls, *args, **temp)
2355 result.extra = extra
2356 return result
2357
2358 def newmeth(self, start):
2359 return start + self.hour + self.second
2360
2361 args = 4, 5, 6
2362
2363 dt1 = self.theclass(*args)
2364 dt2 = C(*args, **{'extra': 7})
2365
2366 self.assertEqual(dt2.__class__, C)
2367 self.assertEqual(dt2.theAnswer, 42)
2368 self.assertEqual(dt2.extra, 7)
2369 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2370 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2371
2372 def test_backdoor_resistance(self):
2373 # see TestDate.test_backdoor_resistance().
2374 base = '2:59.0'
2375 for hour_byte in ' ', '9', chr(24), '\xff':
2376 self.assertRaises(TypeError, self.theclass,
2377 hour_byte + base[1:])
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04002378 # Good bytes, but bad tzinfo:
2379 with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
2380 self.theclass(bytes([1] * len(base)), 'EST')
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002381
2382# A mixin for classes with a tzinfo= argument. Subclasses must define
2383# theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever)
2384# must be legit (which is true for time and datetime).
2385class TZInfoBase:
2386
2387 def test_argument_passing(self):
2388 cls = self.theclass
2389 # A datetime passes itself on, a time passes None.
2390 class introspective(tzinfo):
2391 def tzname(self, dt): return dt and "real" or "none"
2392 def utcoffset(self, dt):
2393 return timedelta(minutes = dt and 42 or -42)
2394 dst = utcoffset
2395
2396 obj = cls(1, 2, 3, tzinfo=introspective())
2397
2398 expected = cls is time and "none" or "real"
2399 self.assertEqual(obj.tzname(), expected)
2400
2401 expected = timedelta(minutes=(cls is time and -42 or 42))
2402 self.assertEqual(obj.utcoffset(), expected)
2403 self.assertEqual(obj.dst(), expected)
2404
2405 def test_bad_tzinfo_classes(self):
2406 cls = self.theclass
2407 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
2408
2409 class NiceTry(object):
2410 def __init__(self): pass
2411 def utcoffset(self, dt): pass
2412 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2413
2414 class BetterTry(tzinfo):
2415 def __init__(self): pass
2416 def utcoffset(self, dt): pass
2417 b = BetterTry()
2418 t = cls(1, 1, 1, tzinfo=b)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002419 self.assertIs(t.tzinfo, b)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002420
2421 def test_utc_offset_out_of_bounds(self):
2422 class Edgy(tzinfo):
2423 def __init__(self, offset):
2424 self.offset = timedelta(minutes=offset)
2425 def utcoffset(self, dt):
2426 return self.offset
2427
2428 cls = self.theclass
2429 for offset, legit in ((-1440, False),
2430 (-1439, True),
2431 (1439, True),
2432 (1440, False)):
2433 if cls is time:
2434 t = cls(1, 2, 3, tzinfo=Edgy(offset))
2435 elif cls is datetime:
2436 t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
2437 else:
2438 assert 0, "impossible"
2439 if legit:
2440 aofs = abs(offset)
2441 h, m = divmod(aofs, 60)
2442 tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
2443 if isinstance(t, datetime):
2444 t = t.timetz()
2445 self.assertEqual(str(t), "01:02:03" + tag)
2446 else:
2447 self.assertRaises(ValueError, str, t)
2448
2449 def test_tzinfo_classes(self):
2450 cls = self.theclass
2451 class C1(tzinfo):
2452 def utcoffset(self, dt): return None
2453 def dst(self, dt): return None
2454 def tzname(self, dt): return None
2455 for t in (cls(1, 1, 1),
2456 cls(1, 1, 1, tzinfo=None),
2457 cls(1, 1, 1, tzinfo=C1())):
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002458 self.assertIsNone(t.utcoffset())
2459 self.assertIsNone(t.dst())
2460 self.assertIsNone(t.tzname())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002461
2462 class C3(tzinfo):
2463 def utcoffset(self, dt): return timedelta(minutes=-1439)
2464 def dst(self, dt): return timedelta(minutes=1439)
2465 def tzname(self, dt): return "aname"
2466 t = cls(1, 1, 1, tzinfo=C3())
2467 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2468 self.assertEqual(t.dst(), timedelta(minutes=1439))
2469 self.assertEqual(t.tzname(), "aname")
2470
2471 # Wrong types.
2472 class C4(tzinfo):
2473 def utcoffset(self, dt): return "aname"
2474 def dst(self, dt): return 7
2475 def tzname(self, dt): return 0
2476 t = cls(1, 1, 1, tzinfo=C4())
2477 self.assertRaises(TypeError, t.utcoffset)
2478 self.assertRaises(TypeError, t.dst)
2479 self.assertRaises(TypeError, t.tzname)
2480
2481 # Offset out of range.
2482 class C6(tzinfo):
2483 def utcoffset(self, dt): return timedelta(hours=-24)
2484 def dst(self, dt): return timedelta(hours=24)
2485 t = cls(1, 1, 1, tzinfo=C6())
2486 self.assertRaises(ValueError, t.utcoffset)
2487 self.assertRaises(ValueError, t.dst)
2488
2489 # Not a whole number of minutes.
2490 class C7(tzinfo):
2491 def utcoffset(self, dt): return timedelta(seconds=61)
2492 def dst(self, dt): return timedelta(microseconds=-81)
2493 t = cls(1, 1, 1, tzinfo=C7())
2494 self.assertRaises(ValueError, t.utcoffset)
2495 self.assertRaises(ValueError, t.dst)
2496
2497 def test_aware_compare(self):
2498 cls = self.theclass
2499
2500 # Ensure that utcoffset() gets ignored if the comparands have
2501 # the same tzinfo member.
2502 class OperandDependentOffset(tzinfo):
2503 def utcoffset(self, t):
2504 if t.minute < 10:
2505 # d0 and d1 equal after adjustment
2506 return timedelta(minutes=t.minute)
2507 else:
2508 # d2 off in the weeds
2509 return timedelta(minutes=59)
2510
2511 base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
2512 d0 = base.replace(minute=3)
2513 d1 = base.replace(minute=9)
2514 d2 = base.replace(minute=11)
2515 for x in d0, d1, d2:
2516 for y in d0, d1, d2:
2517 for op in lt, le, gt, ge, eq, ne:
2518 got = op(x, y)
2519 expected = op(x.minute, y.minute)
2520 self.assertEqual(got, expected)
2521
2522 # However, if they're different members, uctoffset is not ignored.
2523 # Note that a time can't actually have an operand-depedent offset,
2524 # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
2525 # so skip this test for time.
2526 if cls is not time:
2527 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
2528 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
2529 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
2530 for x in d0, d1, d2:
2531 for y in d0, d1, d2:
2532 got = (x > y) - (x < y)
2533 if (x is d0 or x is d1) and (y is d0 or y is d1):
2534 expected = 0
2535 elif x is y is d2:
2536 expected = 0
2537 elif x is d2:
2538 expected = -1
2539 else:
2540 assert y is d2
2541 expected = 1
2542 self.assertEqual(got, expected)
2543
2544
2545# Testing time objects with a non-None tzinfo.
2546class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
2547 theclass = time
2548
2549 def test_empty(self):
2550 t = self.theclass()
2551 self.assertEqual(t.hour, 0)
2552 self.assertEqual(t.minute, 0)
2553 self.assertEqual(t.second, 0)
2554 self.assertEqual(t.microsecond, 0)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002555 self.assertIsNone(t.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002556
2557 def test_zones(self):
2558 est = FixedOffset(-300, "EST", 1)
2559 utc = FixedOffset(0, "UTC", -2)
2560 met = FixedOffset(60, "MET", 3)
2561 t1 = time( 7, 47, tzinfo=est)
2562 t2 = time(12, 47, tzinfo=utc)
2563 t3 = time(13, 47, tzinfo=met)
2564 t4 = time(microsecond=40)
2565 t5 = time(microsecond=40, tzinfo=utc)
2566
2567 self.assertEqual(t1.tzinfo, est)
2568 self.assertEqual(t2.tzinfo, utc)
2569 self.assertEqual(t3.tzinfo, met)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002570 self.assertIsNone(t4.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002571 self.assertEqual(t5.tzinfo, utc)
2572
2573 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2574 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2575 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002576 self.assertIsNone(t4.utcoffset())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002577 self.assertRaises(TypeError, t1.utcoffset, "no args")
2578
2579 self.assertEqual(t1.tzname(), "EST")
2580 self.assertEqual(t2.tzname(), "UTC")
2581 self.assertEqual(t3.tzname(), "MET")
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002582 self.assertIsNone(t4.tzname())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002583 self.assertRaises(TypeError, t1.tzname, "no args")
2584
2585 self.assertEqual(t1.dst(), timedelta(minutes=1))
2586 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2587 self.assertEqual(t3.dst(), timedelta(minutes=3))
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002588 self.assertIsNone(t4.dst())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002589 self.assertRaises(TypeError, t1.dst, "no args")
2590
2591 self.assertEqual(hash(t1), hash(t2))
2592 self.assertEqual(hash(t1), hash(t3))
2593 self.assertEqual(hash(t2), hash(t3))
2594
2595 self.assertEqual(t1, t2)
2596 self.assertEqual(t1, t3)
2597 self.assertEqual(t2, t3)
Alexander Belopolsky08313822012-06-15 20:19:47 -04002598 self.assertNotEqual(t4, t5) # mixed tz-aware & naive
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002599 self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
2600 self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
2601
2602 self.assertEqual(str(t1), "07:47:00-05:00")
2603 self.assertEqual(str(t2), "12:47:00+00:00")
2604 self.assertEqual(str(t3), "13:47:00+01:00")
2605 self.assertEqual(str(t4), "00:00:00.000040")
2606 self.assertEqual(str(t5), "00:00:00.000040+00:00")
2607
2608 self.assertEqual(t1.isoformat(), "07:47:00-05:00")
2609 self.assertEqual(t2.isoformat(), "12:47:00+00:00")
2610 self.assertEqual(t3.isoformat(), "13:47:00+01:00")
2611 self.assertEqual(t4.isoformat(), "00:00:00.000040")
2612 self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
2613
2614 d = 'datetime.time'
2615 self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
2616 self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
2617 self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
2618 self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
2619 self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
2620
2621 self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
2622 "07:47:00 %Z=EST %z=-0500")
2623 self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
2624 self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
2625
2626 yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
2627 t1 = time(23, 59, tzinfo=yuck)
2628 self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
2629 "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
2630
2631 # Check that an invalid tzname result raises an exception.
2632 class Badtzname(tzinfo):
Alexander Belopolskye239d232010-12-08 23:31:48 +00002633 tz = 42
2634 def tzname(self, dt): return self.tz
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002635 t = time(2, 3, 4, tzinfo=Badtzname())
2636 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2637 self.assertRaises(TypeError, t.strftime, "%Z")
2638
Alexander Belopolskye239d232010-12-08 23:31:48 +00002639 # Issue #6697:
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04002640 if '_Fast' in str(self):
Alexander Belopolskye239d232010-12-08 23:31:48 +00002641 Badtzname.tz = '\ud800'
2642 self.assertRaises(ValueError, t.strftime, "%Z")
2643
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002644 def test_hash_edge_cases(self):
2645 # Offsets that overflow a basic time.
2646 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2647 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2648 self.assertEqual(hash(t1), hash(t2))
2649
2650 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
2651 t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
2652 self.assertEqual(hash(t1), hash(t2))
2653
2654 def test_pickling(self):
2655 # Try one without a tzinfo.
2656 args = 20, 59, 16, 64**2
2657 orig = self.theclass(*args)
2658 for pickler, unpickler, proto in pickle_choices:
2659 green = pickler.dumps(orig, proto)
2660 derived = unpickler.loads(green)
2661 self.assertEqual(orig, derived)
2662
2663 # Try one with a tzinfo.
2664 tinfo = PicklableFixedOffset(-300, 'cookie')
2665 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
2666 for pickler, unpickler, proto in pickle_choices:
2667 green = pickler.dumps(orig, proto)
2668 derived = unpickler.loads(green)
2669 self.assertEqual(orig, derived)
2670 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
2671 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2672 self.assertEqual(derived.tzname(), 'cookie')
2673
2674 def test_more_bool(self):
Benjamin Petersonee6bdc02014-03-20 18:00:35 -05002675 # time is always True.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002676 cls = self.theclass
2677
2678 t = cls(0, tzinfo=FixedOffset(-300, ""))
2679 self.assertTrue(t)
2680
2681 t = cls(5, tzinfo=FixedOffset(-300, ""))
2682 self.assertTrue(t)
2683
2684 t = cls(5, tzinfo=FixedOffset(300, ""))
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002685 self.assertTrue(t)
2686
Benjamin Petersonee6bdc02014-03-20 18:00:35 -05002687 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2688 self.assertTrue(t)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002689
2690 def test_replace(self):
2691 cls = self.theclass
2692 z100 = FixedOffset(100, "+100")
2693 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2694 args = [1, 2, 3, 4, z100]
2695 base = cls(*args)
2696 self.assertEqual(base, base.replace())
2697
2698 i = 0
2699 for name, newval in (("hour", 5),
2700 ("minute", 6),
2701 ("second", 7),
2702 ("microsecond", 8),
2703 ("tzinfo", zm200)):
2704 newargs = args[:]
2705 newargs[i] = newval
2706 expected = cls(*newargs)
2707 got = base.replace(**{name: newval})
2708 self.assertEqual(expected, got)
2709 i += 1
2710
2711 # Ensure we can get rid of a tzinfo.
2712 self.assertEqual(base.tzname(), "+100")
2713 base2 = base.replace(tzinfo=None)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002714 self.assertIsNone(base2.tzinfo)
2715 self.assertIsNone(base2.tzname())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002716
2717 # Ensure we can add one.
2718 base3 = base2.replace(tzinfo=z100)
2719 self.assertEqual(base, base3)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002720 self.assertIs(base.tzinfo, base3.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002721
2722 # Out of bounds.
2723 base = cls(1)
2724 self.assertRaises(ValueError, base.replace, hour=24)
2725 self.assertRaises(ValueError, base.replace, minute=-1)
2726 self.assertRaises(ValueError, base.replace, second=100)
2727 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2728
2729 def test_mixed_compare(self):
2730 t1 = time(1, 2, 3)
2731 t2 = time(1, 2, 3)
2732 self.assertEqual(t1, t2)
2733 t2 = t2.replace(tzinfo=None)
2734 self.assertEqual(t1, t2)
2735 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
2736 self.assertEqual(t1, t2)
2737 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
Alexander Belopolsky08313822012-06-15 20:19:47 -04002738 self.assertNotEqual(t1, t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002739
2740 # In time w/ identical tzinfo objects, utcoffset is ignored.
2741 class Varies(tzinfo):
2742 def __init__(self):
2743 self.offset = timedelta(minutes=22)
2744 def utcoffset(self, t):
2745 self.offset += timedelta(minutes=1)
2746 return self.offset
2747
2748 v = Varies()
2749 t1 = t2.replace(tzinfo=v)
2750 t2 = t2.replace(tzinfo=v)
2751 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
2752 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
2753 self.assertEqual(t1, t2)
2754
2755 # But if they're not identical, it isn't ignored.
2756 t2 = t2.replace(tzinfo=Varies())
2757 self.assertTrue(t1 < t2) # t1's offset counter still going up
2758
2759 def test_subclass_timetz(self):
2760
2761 class C(self.theclass):
2762 theAnswer = 42
2763
2764 def __new__(cls, *args, **kws):
2765 temp = kws.copy()
2766 extra = temp.pop('extra')
2767 result = self.theclass.__new__(cls, *args, **temp)
2768 result.extra = extra
2769 return result
2770
2771 def newmeth(self, start):
2772 return start + self.hour + self.second
2773
2774 args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
2775
2776 dt1 = self.theclass(*args)
2777 dt2 = C(*args, **{'extra': 7})
2778
2779 self.assertEqual(dt2.__class__, C)
2780 self.assertEqual(dt2.theAnswer, 42)
2781 self.assertEqual(dt2.extra, 7)
2782 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
2783 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2784
2785
2786# Testing datetime objects with a non-None tzinfo.
2787
2788class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
2789 theclass = datetime
2790
2791 def test_trivial(self):
2792 dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
2793 self.assertEqual(dt.year, 1)
2794 self.assertEqual(dt.month, 2)
2795 self.assertEqual(dt.day, 3)
2796 self.assertEqual(dt.hour, 4)
2797 self.assertEqual(dt.minute, 5)
2798 self.assertEqual(dt.second, 6)
2799 self.assertEqual(dt.microsecond, 7)
2800 self.assertEqual(dt.tzinfo, None)
2801
2802 def test_even_more_compare(self):
2803 # The test_compare() and test_more_compare() inherited from TestDate
2804 # and TestDateTime covered non-tzinfo cases.
2805
2806 # Smallest possible after UTC adjustment.
2807 t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2808 # Largest possible after UTC adjustment.
2809 t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2810 tzinfo=FixedOffset(-1439, ""))
2811
2812 # Make sure those compare correctly, and w/o overflow.
2813 self.assertTrue(t1 < t2)
2814 self.assertTrue(t1 != t2)
2815 self.assertTrue(t2 > t1)
2816
2817 self.assertEqual(t1, t1)
2818 self.assertEqual(t2, t2)
2819
2820 # Equal afer adjustment.
2821 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
2822 t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
2823 self.assertEqual(t1, t2)
2824
2825 # Change t1 not to subtract a minute, and t1 should be larger.
2826 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
2827 self.assertTrue(t1 > t2)
2828
2829 # Change t1 to subtract 2 minutes, and t1 should be smaller.
2830 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
2831 self.assertTrue(t1 < t2)
2832
2833 # Back to the original t1, but make seconds resolve it.
2834 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2835 second=1)
2836 self.assertTrue(t1 > t2)
2837
2838 # Likewise, but make microseconds resolve it.
2839 t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
2840 microsecond=1)
2841 self.assertTrue(t1 > t2)
2842
Alexander Belopolsky08313822012-06-15 20:19:47 -04002843 # Make t2 naive and it should differ.
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002844 t2 = self.theclass.min
Alexander Belopolsky08313822012-06-15 20:19:47 -04002845 self.assertNotEqual(t1, t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002846 self.assertEqual(t2, t2)
2847
2848 # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
2849 class Naive(tzinfo):
2850 def utcoffset(self, dt): return None
2851 t2 = self.theclass(5, 6, 7, tzinfo=Naive())
Alexander Belopolsky08313822012-06-15 20:19:47 -04002852 self.assertNotEqual(t1, t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002853 self.assertEqual(t2, t2)
2854
2855 # OTOH, it's OK to compare two of these mixing the two ways of being
2856 # naive.
2857 t1 = self.theclass(5, 6, 7)
2858 self.assertEqual(t1, t2)
2859
2860 # Try a bogus uctoffset.
2861 class Bogus(tzinfo):
2862 def utcoffset(self, dt):
2863 return timedelta(minutes=1440) # out of bounds
2864 t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
2865 t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
2866 self.assertRaises(ValueError, lambda: t1 == t2)
2867
2868 def test_pickling(self):
2869 # Try one without a tzinfo.
2870 args = 6, 7, 23, 20, 59, 1, 64**2
2871 orig = self.theclass(*args)
2872 for pickler, unpickler, proto in pickle_choices:
2873 green = pickler.dumps(orig, proto)
2874 derived = unpickler.loads(green)
2875 self.assertEqual(orig, derived)
2876
2877 # Try one with a tzinfo.
2878 tinfo = PicklableFixedOffset(-300, 'cookie')
2879 orig = self.theclass(*args, **{'tzinfo': tinfo})
2880 derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
2881 for pickler, unpickler, proto in pickle_choices:
2882 green = pickler.dumps(orig, proto)
2883 derived = unpickler.loads(green)
2884 self.assertEqual(orig, derived)
2885 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
2886 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2887 self.assertEqual(derived.tzname(), 'cookie')
2888
2889 def test_extreme_hashes(self):
2890 # If an attempt is made to hash these via subtracting the offset
2891 # then hashing a datetime object, OverflowError results. The
2892 # Python implementation used to blow up here.
2893 t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
2894 hash(t)
2895 t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
2896 tzinfo=FixedOffset(-1439, ""))
2897 hash(t)
2898
2899 # OTOH, an OOB offset should blow up.
2900 t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
2901 self.assertRaises(ValueError, hash, t)
2902
2903 def test_zones(self):
2904 est = FixedOffset(-300, "EST")
2905 utc = FixedOffset(0, "UTC")
2906 met = FixedOffset(60, "MET")
2907 t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
2908 t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
2909 t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
2910 self.assertEqual(t1.tzinfo, est)
2911 self.assertEqual(t2.tzinfo, utc)
2912 self.assertEqual(t3.tzinfo, met)
2913 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2914 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2915 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
2916 self.assertEqual(t1.tzname(), "EST")
2917 self.assertEqual(t2.tzname(), "UTC")
2918 self.assertEqual(t3.tzname(), "MET")
2919 self.assertEqual(hash(t1), hash(t2))
2920 self.assertEqual(hash(t1), hash(t3))
2921 self.assertEqual(hash(t2), hash(t3))
2922 self.assertEqual(t1, t2)
2923 self.assertEqual(t1, t3)
2924 self.assertEqual(t2, t3)
2925 self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
2926 self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
2927 self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
2928 d = 'datetime.datetime(2002, 3, 19, '
2929 self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
2930 self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
2931 self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
2932
2933 def test_combine(self):
2934 met = FixedOffset(60, "MET")
2935 d = date(2002, 3, 4)
2936 tz = time(18, 45, 3, 1234, tzinfo=met)
2937 dt = datetime.combine(d, tz)
2938 self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
2939 tzinfo=met))
2940
2941 def test_extract(self):
2942 met = FixedOffset(60, "MET")
2943 dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
2944 self.assertEqual(dt.date(), date(2002, 3, 4))
2945 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
2946 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
2947
2948 def test_tz_aware_arithmetic(self):
2949 import random
2950
2951 now = self.theclass.now()
2952 tz55 = FixedOffset(-330, "west 5:30")
2953 timeaware = now.time().replace(tzinfo=tz55)
2954 nowaware = self.theclass.combine(now.date(), timeaware)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002955 self.assertIs(nowaware.tzinfo, tz55)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002956 self.assertEqual(nowaware.timetz(), timeaware)
2957
2958 # Can't mix aware and non-aware.
2959 self.assertRaises(TypeError, lambda: now - nowaware)
2960 self.assertRaises(TypeError, lambda: nowaware - now)
2961
2962 # And adding datetime's doesn't make sense, aware or not.
2963 self.assertRaises(TypeError, lambda: now + nowaware)
2964 self.assertRaises(TypeError, lambda: nowaware + now)
2965 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2966
2967 # Subtracting should yield 0.
2968 self.assertEqual(now - now, timedelta(0))
2969 self.assertEqual(nowaware - nowaware, timedelta(0))
2970
2971 # Adding a delta should preserve tzinfo.
2972 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2973 nowawareplus = nowaware + delta
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002974 self.assertIs(nowaware.tzinfo, tz55)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002975 nowawareplus2 = delta + nowaware
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002976 self.assertIs(nowawareplus2.tzinfo, tz55)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002977 self.assertEqual(nowawareplus, nowawareplus2)
2978
2979 # that - delta should be what we started with, and that - what we
2980 # started with should be delta.
2981 diff = nowawareplus - delta
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002982 self.assertIs(diff.tzinfo, tz55)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002983 self.assertEqual(nowaware, diff)
2984 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2985 self.assertEqual(nowawareplus - nowaware, delta)
2986
2987 # Make up a random timezone.
2988 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
2989 # Attach it to nowawareplus.
2990 nowawareplus = nowawareplus.replace(tzinfo=tzr)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02002991 self.assertIs(nowawareplus.tzinfo, tzr)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00002992 # Make sure the difference takes the timezone adjustments into account.
2993 got = nowaware - nowawareplus
2994 # Expected: (nowaware base - nowaware offset) -
2995 # (nowawareplus base - nowawareplus offset) =
2996 # (nowaware base - nowawareplus base) +
2997 # (nowawareplus offset - nowaware offset) =
2998 # -delta + nowawareplus offset - nowaware offset
2999 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
3000 self.assertEqual(got, expected)
3001
3002 # Try max possible difference.
3003 min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
3004 max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
3005 tzinfo=FixedOffset(-1439, "max"))
3006 maxdiff = max - min
3007 self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
3008 timedelta(minutes=2*1439))
3009 # Different tzinfo, but the same offset
3010 tza = timezone(HOUR, 'A')
3011 tzb = timezone(HOUR, 'B')
3012 delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
3013 self.assertEqual(delta, self.theclass.min - self.theclass.max)
3014
3015 def test_tzinfo_now(self):
3016 meth = self.theclass.now
3017 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3018 base = meth()
3019 # Try with and without naming the keyword.
3020 off42 = FixedOffset(42, "42")
3021 another = meth(off42)
3022 again = meth(tz=off42)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003023 self.assertIs(another.tzinfo, again.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003024 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
3025 # Bad argument with and w/o naming the keyword.
3026 self.assertRaises(TypeError, meth, 16)
3027 self.assertRaises(TypeError, meth, tzinfo=16)
3028 # Bad keyword name.
3029 self.assertRaises(TypeError, meth, tinfo=off42)
3030 # Too many args.
3031 self.assertRaises(TypeError, meth, off42, off42)
3032
3033 # We don't know which time zone we're in, and don't have a tzinfo
3034 # class to represent it, so seeing whether a tz argument actually
3035 # does a conversion is tricky.
3036 utc = FixedOffset(0, "utc", 0)
3037 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
3038 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
3039 for dummy in range(3):
3040 now = datetime.now(weirdtz)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003041 self.assertIs(now.tzinfo, weirdtz)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003042 utcnow = datetime.utcnow().replace(tzinfo=utc)
3043 now2 = utcnow.astimezone(weirdtz)
3044 if abs(now - now2) < timedelta(seconds=30):
3045 break
3046 # Else the code is broken, or more than 30 seconds passed between
3047 # calls; assuming the latter, just try again.
3048 else:
3049 # Three strikes and we're out.
3050 self.fail("utcnow(), now(tz), or astimezone() may be broken")
3051
3052 def test_tzinfo_fromtimestamp(self):
3053 import time
3054 meth = self.theclass.fromtimestamp
3055 ts = time.time()
3056 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3057 base = meth(ts)
3058 # Try with and without naming the keyword.
3059 off42 = FixedOffset(42, "42")
3060 another = meth(ts, off42)
3061 again = meth(ts, tz=off42)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003062 self.assertIs(another.tzinfo, again.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003063 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
3064 # Bad argument with and w/o naming the keyword.
3065 self.assertRaises(TypeError, meth, ts, 16)
3066 self.assertRaises(TypeError, meth, ts, tzinfo=16)
3067 # Bad keyword name.
3068 self.assertRaises(TypeError, meth, ts, tinfo=off42)
3069 # Too many args.
3070 self.assertRaises(TypeError, meth, ts, off42, off42)
3071 # Too few args.
3072 self.assertRaises(TypeError, meth)
3073
3074 # Try to make sure tz= actually does some conversion.
3075 timestamp = 1000000000
3076 utcdatetime = datetime.utcfromtimestamp(timestamp)
3077 # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
3078 # But on some flavor of Mac, it's nowhere near that. So we can't have
3079 # any idea here what time that actually is, we can only test that
3080 # relative changes match.
3081 utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
3082 tz = FixedOffset(utcoffset, "tz", 0)
3083 expected = utcdatetime + utcoffset
3084 got = datetime.fromtimestamp(timestamp, tz)
3085 self.assertEqual(expected, got.replace(tzinfo=None))
3086
3087 def test_tzinfo_utcnow(self):
3088 meth = self.theclass.utcnow
3089 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3090 base = meth()
3091 # Try with and without naming the keyword; for whatever reason,
3092 # utcnow() doesn't accept a tzinfo argument.
3093 off42 = FixedOffset(42, "42")
3094 self.assertRaises(TypeError, meth, off42)
3095 self.assertRaises(TypeError, meth, tzinfo=off42)
3096
3097 def test_tzinfo_utcfromtimestamp(self):
3098 import time
3099 meth = self.theclass.utcfromtimestamp
3100 ts = time.time()
3101 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3102 base = meth(ts)
3103 # Try with and without naming the keyword; for whatever reason,
3104 # utcfromtimestamp() doesn't accept a tzinfo argument.
3105 off42 = FixedOffset(42, "42")
3106 self.assertRaises(TypeError, meth, ts, off42)
3107 self.assertRaises(TypeError, meth, ts, tzinfo=off42)
3108
3109 def test_tzinfo_timetuple(self):
3110 # TestDateTime tested most of this. datetime adds a twist to the
3111 # DST flag.
3112 class DST(tzinfo):
3113 def __init__(self, dstvalue):
3114 if isinstance(dstvalue, int):
3115 dstvalue = timedelta(minutes=dstvalue)
3116 self.dstvalue = dstvalue
3117 def dst(self, dt):
3118 return self.dstvalue
3119
3120 cls = self.theclass
3121 for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
3122 d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
3123 t = d.timetuple()
3124 self.assertEqual(1, t.tm_year)
3125 self.assertEqual(1, t.tm_mon)
3126 self.assertEqual(1, t.tm_mday)
3127 self.assertEqual(10, t.tm_hour)
3128 self.assertEqual(20, t.tm_min)
3129 self.assertEqual(30, t.tm_sec)
3130 self.assertEqual(0, t.tm_wday)
3131 self.assertEqual(1, t.tm_yday)
3132 self.assertEqual(flag, t.tm_isdst)
3133
3134 # dst() returns wrong type.
3135 self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
3136
3137 # dst() at the edge.
3138 self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
3139 self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
3140
3141 # dst() out of range.
3142 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
3143 self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
3144
3145 def test_utctimetuple(self):
3146 class DST(tzinfo):
3147 def __init__(self, dstvalue=0):
3148 if isinstance(dstvalue, int):
3149 dstvalue = timedelta(minutes=dstvalue)
3150 self.dstvalue = dstvalue
3151 def dst(self, dt):
3152 return self.dstvalue
3153
3154 cls = self.theclass
3155 # This can't work: DST didn't implement utcoffset.
3156 self.assertRaises(NotImplementedError,
3157 cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
3158
3159 class UOFS(DST):
3160 def __init__(self, uofs, dofs=None):
3161 DST.__init__(self, dofs)
3162 self.uofs = timedelta(minutes=uofs)
3163 def utcoffset(self, dt):
3164 return self.uofs
3165
3166 for dstvalue in -33, 33, 0, None:
3167 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
3168 t = d.utctimetuple()
3169 self.assertEqual(d.year, t.tm_year)
3170 self.assertEqual(d.month, t.tm_mon)
3171 self.assertEqual(d.day, t.tm_mday)
3172 self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
3173 self.assertEqual(13, t.tm_min)
3174 self.assertEqual(d.second, t.tm_sec)
3175 self.assertEqual(d.weekday(), t.tm_wday)
3176 self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
3177 t.tm_yday)
3178 # Ensure tm_isdst is 0 regardless of what dst() says: DST
3179 # is never in effect for a UTC time.
3180 self.assertEqual(0, t.tm_isdst)
3181
3182 # For naive datetime, utctimetuple == timetuple except for isdst
3183 d = cls(1, 2, 3, 10, 20, 30, 40)
3184 t = d.utctimetuple()
3185 self.assertEqual(t[:-1], d.timetuple()[:-1])
3186 self.assertEqual(0, t.tm_isdst)
3187 # Same if utcoffset is None
3188 class NOFS(DST):
3189 def utcoffset(self, dt):
3190 return None
3191 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
3192 t = d.utctimetuple()
3193 self.assertEqual(t[:-1], d.timetuple()[:-1])
3194 self.assertEqual(0, t.tm_isdst)
3195 # Check that bad tzinfo is detected
3196 class BOFS(DST):
3197 def utcoffset(self, dt):
3198 return "EST"
3199 d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
3200 self.assertRaises(TypeError, d.utctimetuple)
3201
3202 # Check that utctimetuple() is the same as
3203 # astimezone(utc).timetuple()
3204 d = cls(2010, 11, 13, 14, 15, 16, 171819)
3205 for tz in [timezone.min, timezone.utc, timezone.max]:
3206 dtz = d.replace(tzinfo=tz)
3207 self.assertEqual(dtz.utctimetuple()[:-1],
3208 dtz.astimezone(timezone.utc).timetuple()[:-1])
3209 # At the edges, UTC adjustment can produce years out-of-range
3210 # for a datetime object. Ensure that an OverflowError is
3211 # raised.
3212 tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
3213 # That goes back 1 minute less than a full day.
3214 self.assertRaises(OverflowError, tiny.utctimetuple)
3215
3216 huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
3217 # That goes forward 1 minute less than a full day.
3218 self.assertRaises(OverflowError, huge.utctimetuple)
3219 # More overflow cases
3220 tiny = cls.min.replace(tzinfo=timezone(MINUTE))
3221 self.assertRaises(OverflowError, tiny.utctimetuple)
3222 huge = cls.max.replace(tzinfo=timezone(-MINUTE))
3223 self.assertRaises(OverflowError, huge.utctimetuple)
3224
3225 def test_tzinfo_isoformat(self):
3226 zero = FixedOffset(0, "+00:00")
3227 plus = FixedOffset(220, "+03:40")
3228 minus = FixedOffset(-231, "-03:51")
3229 unknown = FixedOffset(None, "")
3230
3231 cls = self.theclass
3232 datestr = '0001-02-03'
3233 for ofs in None, zero, plus, minus, unknown:
3234 for us in 0, 987001:
3235 d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
3236 timestr = '04:05:59' + (us and '.987001' or '')
3237 ofsstr = ofs is not None and d.tzname() or ''
3238 tailstr = timestr + ofsstr
3239 iso = d.isoformat()
3240 self.assertEqual(iso, datestr + 'T' + tailstr)
3241 self.assertEqual(iso, d.isoformat('T'))
3242 self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
3243 self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
3244 self.assertEqual(str(d), datestr + ' ' + tailstr)
3245
3246 def test_replace(self):
3247 cls = self.theclass
3248 z100 = FixedOffset(100, "+100")
3249 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
3250 args = [1, 2, 3, 4, 5, 6, 7, z100]
3251 base = cls(*args)
3252 self.assertEqual(base, base.replace())
3253
3254 i = 0
3255 for name, newval in (("year", 2),
3256 ("month", 3),
3257 ("day", 4),
3258 ("hour", 5),
3259 ("minute", 6),
3260 ("second", 7),
3261 ("microsecond", 8),
3262 ("tzinfo", zm200)):
3263 newargs = args[:]
3264 newargs[i] = newval
3265 expected = cls(*newargs)
3266 got = base.replace(**{name: newval})
3267 self.assertEqual(expected, got)
3268 i += 1
3269
3270 # Ensure we can get rid of a tzinfo.
3271 self.assertEqual(base.tzname(), "+100")
3272 base2 = base.replace(tzinfo=None)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003273 self.assertIsNone(base2.tzinfo)
3274 self.assertIsNone(base2.tzname())
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003275
3276 # Ensure we can add one.
3277 base3 = base2.replace(tzinfo=z100)
3278 self.assertEqual(base, base3)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003279 self.assertIs(base.tzinfo, base3.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003280
3281 # Out of bounds.
3282 base = cls(2000, 2, 29)
3283 self.assertRaises(ValueError, base.replace, year=2001)
3284
3285 def test_more_astimezone(self):
3286 # The inherited test_astimezone covered some trivial and error cases.
3287 fnone = FixedOffset(None, "None")
3288 f44m = FixedOffset(44, "44")
3289 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3290
3291 dt = self.theclass.now(tz=f44m)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003292 self.assertIs(dt.tzinfo, f44m)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003293 # Replacing with degenerate tzinfo raises an exception.
3294 self.assertRaises(ValueError, dt.astimezone, fnone)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003295 # Replacing with same tzinfo makes no change.
3296 x = dt.astimezone(dt.tzinfo)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003297 self.assertIs(x.tzinfo, f44m)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003298 self.assertEqual(x.date(), dt.date())
3299 self.assertEqual(x.time(), dt.time())
3300
3301 # Replacing with different tzinfo does adjust.
3302 got = dt.astimezone(fm5h)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003303 self.assertIs(got.tzinfo, fm5h)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003304 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3305 expected = dt - dt.utcoffset() # in effect, convert to UTC
3306 expected += fm5h.utcoffset(dt) # and from there to local time
3307 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3308 self.assertEqual(got.date(), expected.date())
3309 self.assertEqual(got.time(), expected.time())
3310 self.assertEqual(got.timetz(), expected.timetz())
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003311 self.assertIs(got.tzinfo, expected.tzinfo)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003312 self.assertEqual(got, expected)
3313
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04003314 @support.run_with_tz('UTC')
3315 def test_astimezone_default_utc(self):
3316 dt = self.theclass.now(timezone.utc)
3317 self.assertEqual(dt.astimezone(None), dt)
3318 self.assertEqual(dt.astimezone(), dt)
3319
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04003320 # Note that offset in TZ variable has the opposite sign to that
3321 # produced by %z directive.
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04003322 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
3323 def test_astimezone_default_eastern(self):
3324 dt = self.theclass(2012, 11, 4, 6, 30, tzinfo=timezone.utc)
3325 local = dt.astimezone()
3326 self.assertEqual(dt, local)
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04003327 self.assertEqual(local.strftime("%z %Z"), "-0500 EST")
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04003328 dt = self.theclass(2012, 11, 4, 5, 30, tzinfo=timezone.utc)
3329 local = dt.astimezone()
3330 self.assertEqual(dt, local)
Alexander Belopolsky93c9cd02012-06-22 16:04:19 -04003331 self.assertEqual(local.strftime("%z %Z"), "-0400 EDT")
Alexander Belopolskyfdc860f2012-06-22 12:23:23 -04003332
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003333 def test_aware_subtract(self):
3334 cls = self.theclass
3335
3336 # Ensure that utcoffset() is ignored when the operands have the
3337 # same tzinfo member.
3338 class OperandDependentOffset(tzinfo):
3339 def utcoffset(self, t):
3340 if t.minute < 10:
3341 # d0 and d1 equal after adjustment
3342 return timedelta(minutes=t.minute)
3343 else:
3344 # d2 off in the weeds
3345 return timedelta(minutes=59)
3346
3347 base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
3348 d0 = base.replace(minute=3)
3349 d1 = base.replace(minute=9)
3350 d2 = base.replace(minute=11)
3351 for x in d0, d1, d2:
3352 for y in d0, d1, d2:
3353 got = x - y
3354 expected = timedelta(minutes=x.minute - y.minute)
3355 self.assertEqual(got, expected)
3356
3357 # OTOH, if the tzinfo members are distinct, utcoffsets aren't
3358 # ignored.
3359 base = cls(8, 9, 10, 11, 12, 13, 14)
3360 d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
3361 d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
3362 d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
3363 for x in d0, d1, d2:
3364 for y in d0, d1, d2:
3365 got = x - y
3366 if (x is d0 or x is d1) and (y is d0 or y is d1):
3367 expected = timedelta(0)
3368 elif x is y is d2:
3369 expected = timedelta(0)
3370 elif x is d2:
3371 expected = timedelta(minutes=(11-59)-0)
3372 else:
3373 assert y is d2
3374 expected = timedelta(minutes=0-(11-59))
3375 self.assertEqual(got, expected)
3376
3377 def test_mixed_compare(self):
3378 t1 = datetime(1, 2, 3, 4, 5, 6, 7)
3379 t2 = datetime(1, 2, 3, 4, 5, 6, 7)
3380 self.assertEqual(t1, t2)
3381 t2 = t2.replace(tzinfo=None)
3382 self.assertEqual(t1, t2)
3383 t2 = t2.replace(tzinfo=FixedOffset(None, ""))
3384 self.assertEqual(t1, t2)
3385 t2 = t2.replace(tzinfo=FixedOffset(0, ""))
Alexander Belopolsky08313822012-06-15 20:19:47 -04003386 self.assertNotEqual(t1, t2)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003387
3388 # In datetime w/ identical tzinfo objects, utcoffset is ignored.
3389 class Varies(tzinfo):
3390 def __init__(self):
3391 self.offset = timedelta(minutes=22)
3392 def utcoffset(self, t):
3393 self.offset += timedelta(minutes=1)
3394 return self.offset
3395
3396 v = Varies()
3397 t1 = t2.replace(tzinfo=v)
3398 t2 = t2.replace(tzinfo=v)
3399 self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
3400 self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
3401 self.assertEqual(t1, t2)
3402
3403 # But if they're not identical, it isn't ignored.
3404 t2 = t2.replace(tzinfo=Varies())
3405 self.assertTrue(t1 < t2) # t1's offset counter still going up
3406
3407 def test_subclass_datetimetz(self):
3408
3409 class C(self.theclass):
3410 theAnswer = 42
3411
3412 def __new__(cls, *args, **kws):
3413 temp = kws.copy()
3414 extra = temp.pop('extra')
3415 result = self.theclass.__new__(cls, *args, **temp)
3416 result.extra = extra
3417 return result
3418
3419 def newmeth(self, start):
3420 return start + self.hour + self.year
3421
3422 args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
3423
3424 dt1 = self.theclass(*args)
3425 dt2 = C(*args, **{'extra': 7})
3426
3427 self.assertEqual(dt2.__class__, C)
3428 self.assertEqual(dt2.theAnswer, 42)
3429 self.assertEqual(dt2.extra, 7)
3430 self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
3431 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
3432
3433# Pain to set up DST-aware tzinfo classes.
3434
3435def first_sunday_on_or_after(dt):
3436 days_to_go = 6 - dt.weekday()
3437 if days_to_go:
3438 dt += timedelta(days_to_go)
3439 return dt
3440
3441ZERO = timedelta(0)
3442MINUTE = timedelta(minutes=1)
3443HOUR = timedelta(hours=1)
3444DAY = timedelta(days=1)
3445# In the US, DST starts at 2am (standard time) on the first Sunday in April.
3446DSTSTART = datetime(1, 4, 1, 2)
3447# and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
3448# which is the first Sunday on or after Oct 25. Because we view 1:MM as
3449# being standard time on that day, there is no spelling in local time of
3450# the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
3451DSTEND = datetime(1, 10, 25, 1)
3452
3453class USTimeZone(tzinfo):
3454
3455 def __init__(self, hours, reprname, stdname, dstname):
3456 self.stdoffset = timedelta(hours=hours)
3457 self.reprname = reprname
3458 self.stdname = stdname
3459 self.dstname = dstname
3460
3461 def __repr__(self):
3462 return self.reprname
3463
3464 def tzname(self, dt):
3465 if self.dst(dt):
3466 return self.dstname
3467 else:
3468 return self.stdname
3469
3470 def utcoffset(self, dt):
3471 return self.stdoffset + self.dst(dt)
3472
3473 def dst(self, dt):
3474 if dt is None or dt.tzinfo is None:
3475 # An exception instead may be sensible here, in one or more of
3476 # the cases.
3477 return ZERO
3478 assert dt.tzinfo is self
3479
3480 # Find first Sunday in April.
3481 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
3482 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
3483
3484 # Find last Sunday in October.
3485 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
3486 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
3487
3488 # Can't compare naive to aware objects, so strip the timezone from
3489 # dt first.
3490 if start <= dt.replace(tzinfo=None) < end:
3491 return HOUR
3492 else:
3493 return ZERO
3494
3495Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
3496Central = USTimeZone(-6, "Central", "CST", "CDT")
3497Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
3498Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
3499utc_real = FixedOffset(0, "UTC", 0)
3500# For better test coverage, we want another flavor of UTC that's west of
3501# the Eastern and Pacific timezones.
3502utc_fake = FixedOffset(-12*60, "UTCfake", 0)
3503
3504class TestTimezoneConversions(unittest.TestCase):
3505 # The DST switch times for 2002, in std time.
3506 dston = datetime(2002, 4, 7, 2)
3507 dstoff = datetime(2002, 10, 27, 1)
3508
3509 theclass = datetime
3510
3511 # Check a time that's inside DST.
3512 def checkinside(self, dt, tz, utc, dston, dstoff):
3513 self.assertEqual(dt.dst(), HOUR)
3514
3515 # Conversion to our own timezone is always an identity.
3516 self.assertEqual(dt.astimezone(tz), dt)
3517
3518 asutc = dt.astimezone(utc)
3519 there_and_back = asutc.astimezone(tz)
3520
3521 # Conversion to UTC and back isn't always an identity here,
3522 # because there are redundant spellings (in local time) of
3523 # UTC time when DST begins: the clock jumps from 1:59:59
3524 # to 3:00:00, and a local time of 2:MM:SS doesn't really
3525 # make sense then. The classes above treat 2:MM:SS as
3526 # daylight time then (it's "after 2am"), really an alias
3527 # for 1:MM:SS standard time. The latter form is what
3528 # conversion back from UTC produces.
3529 if dt.date() == dston.date() and dt.hour == 2:
3530 # We're in the redundant hour, and coming back from
3531 # UTC gives the 1:MM:SS standard-time spelling.
3532 self.assertEqual(there_and_back + HOUR, dt)
3533 # Although during was considered to be in daylight
3534 # time, there_and_back is not.
3535 self.assertEqual(there_and_back.dst(), ZERO)
3536 # They're the same times in UTC.
3537 self.assertEqual(there_and_back.astimezone(utc),
3538 dt.astimezone(utc))
3539 else:
3540 # We're not in the redundant hour.
3541 self.assertEqual(dt, there_and_back)
3542
3543 # Because we have a redundant spelling when DST begins, there is
Ezio Melotti3b3499b2011-03-16 11:35:38 +02003544 # (unfortunately) an hour when DST ends that can't be spelled at all in
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003545 # local time. When DST ends, the clock jumps from 1:59 back to 1:00
3546 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
3547 # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
3548 # daylight time. The hour 1:MM daylight == 0:MM standard can't be
3549 # expressed in local time. Nevertheless, we want conversion back
3550 # from UTC to mimic the local clock's "repeat an hour" behavior.
3551 nexthour_utc = asutc + HOUR
3552 nexthour_tz = nexthour_utc.astimezone(tz)
3553 if dt.date() == dstoff.date() and dt.hour == 0:
3554 # We're in the hour before the last DST hour. The last DST hour
3555 # is ineffable. We want the conversion back to repeat 1:MM.
3556 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3557 nexthour_utc += HOUR
3558 nexthour_tz = nexthour_utc.astimezone(tz)
3559 self.assertEqual(nexthour_tz, dt.replace(hour=1))
3560 else:
3561 self.assertEqual(nexthour_tz - dt, HOUR)
3562
3563 # Check a time that's outside DST.
3564 def checkoutside(self, dt, tz, utc):
3565 self.assertEqual(dt.dst(), ZERO)
3566
3567 # Conversion to our own timezone is always an identity.
3568 self.assertEqual(dt.astimezone(tz), dt)
3569
3570 # Converting to UTC and back is an identity too.
3571 asutc = dt.astimezone(utc)
3572 there_and_back = asutc.astimezone(tz)
3573 self.assertEqual(dt, there_and_back)
3574
3575 def convert_between_tz_and_utc(self, tz, utc):
3576 dston = self.dston.replace(tzinfo=tz)
3577 # Because 1:MM on the day DST ends is taken as being standard time,
3578 # there is no spelling in tz for the last hour of daylight time.
3579 # For purposes of the test, the last hour of DST is 0:MM, which is
3580 # taken as being daylight time (and 1:MM is taken as being standard
3581 # time).
3582 dstoff = self.dstoff.replace(tzinfo=tz)
3583 for delta in (timedelta(weeks=13),
3584 DAY,
3585 HOUR,
3586 timedelta(minutes=1),
3587 timedelta(microseconds=1)):
3588
3589 self.checkinside(dston, tz, utc, dston, dstoff)
3590 for during in dston + delta, dstoff - delta:
3591 self.checkinside(during, tz, utc, dston, dstoff)
3592
3593 self.checkoutside(dstoff, tz, utc)
3594 for outside in dston - delta, dstoff + delta:
3595 self.checkoutside(outside, tz, utc)
3596
3597 def test_easy(self):
3598 # Despite the name of this test, the endcases are excruciating.
3599 self.convert_between_tz_and_utc(Eastern, utc_real)
3600 self.convert_between_tz_and_utc(Pacific, utc_real)
3601 self.convert_between_tz_and_utc(Eastern, utc_fake)
3602 self.convert_between_tz_and_utc(Pacific, utc_fake)
3603 # The next is really dancing near the edge. It works because
3604 # Pacific and Eastern are far enough apart that their "problem
3605 # hours" don't overlap.
3606 self.convert_between_tz_and_utc(Eastern, Pacific)
3607 self.convert_between_tz_and_utc(Pacific, Eastern)
3608 # OTOH, these fail! Don't enable them. The difficulty is that
3609 # the edge case tests assume that every hour is representable in
3610 # the "utc" class. This is always true for a fixed-offset tzinfo
3611 # class (lke utc_real and utc_fake), but not for Eastern or Central.
3612 # For these adjacent DST-aware time zones, the range of time offsets
3613 # tested ends up creating hours in the one that aren't representable
3614 # in the other. For the same reason, we would see failures in the
3615 # Eastern vs Pacific tests too if we added 3*HOUR to the list of
3616 # offset deltas in convert_between_tz_and_utc().
3617 #
3618 # self.convert_between_tz_and_utc(Eastern, Central) # can't work
3619 # self.convert_between_tz_and_utc(Central, Eastern) # can't work
3620
3621 def test_tricky(self):
3622 # 22:00 on day before daylight starts.
3623 fourback = self.dston - timedelta(hours=4)
3624 ninewest = FixedOffset(-9*60, "-0900", 0)
3625 fourback = fourback.replace(tzinfo=ninewest)
3626 # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
3627 # 2", we should get the 3 spelling.
3628 # If we plug 22:00 the day before into Eastern, it "looks like std
3629 # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
3630 # to 22:00 lands on 2:00, which makes no sense in local time (the
3631 # local clock jumps from 1 to 3). The point here is to make sure we
3632 # get the 3 spelling.
3633 expected = self.dston.replace(hour=3)
3634 got = fourback.astimezone(Eastern).replace(tzinfo=None)
3635 self.assertEqual(expected, got)
3636
3637 # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
3638 # case we want the 1:00 spelling.
3639 sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
3640 # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
3641 # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
3642 # spelling.
3643 expected = self.dston.replace(hour=1)
3644 got = sixutc.astimezone(Eastern).replace(tzinfo=None)
3645 self.assertEqual(expected, got)
3646
3647 # Now on the day DST ends, we want "repeat an hour" behavior.
3648 # UTC 4:MM 5:MM 6:MM 7:MM checking these
3649 # EST 23:MM 0:MM 1:MM 2:MM
3650 # EDT 0:MM 1:MM 2:MM 3:MM
3651 # wall 0:MM 1:MM 1:MM 2:MM against these
3652 for utc in utc_real, utc_fake:
3653 for tz in Eastern, Pacific:
3654 first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
3655 # Convert that to UTC.
3656 first_std_hour -= tz.utcoffset(None)
3657 # Adjust for possibly fake UTC.
3658 asutc = first_std_hour + utc.utcoffset(None)
3659 # First UTC hour to convert; this is 4:00 when utc=utc_real &
3660 # tz=Eastern.
3661 asutcbase = asutc.replace(tzinfo=utc)
3662 for tzhour in (0, 1, 1, 2):
3663 expectedbase = self.dstoff.replace(hour=tzhour)
3664 for minute in 0, 30, 59:
3665 expected = expectedbase.replace(minute=minute)
3666 asutc = asutcbase.replace(minute=minute)
3667 astz = asutc.astimezone(tz)
3668 self.assertEqual(astz.replace(tzinfo=None), expected)
3669 asutcbase += HOUR
3670
3671
3672 def test_bogus_dst(self):
3673 class ok(tzinfo):
3674 def utcoffset(self, dt): return HOUR
3675 def dst(self, dt): return HOUR
3676
3677 now = self.theclass.now().replace(tzinfo=utc_real)
3678 # Doesn't blow up.
3679 now.astimezone(ok())
3680
3681 # Does blow up.
3682 class notok(ok):
3683 def dst(self, dt): return None
3684 self.assertRaises(ValueError, now.astimezone, notok())
3685
3686 # Sometimes blow up. In the following, tzinfo.dst()
3687 # implementation may return None or not None depending on
3688 # whether DST is assumed to be in effect. In this situation,
3689 # a ValueError should be raised by astimezone().
3690 class tricky_notok(ok):
3691 def dst(self, dt):
3692 if dt.year == 2000:
3693 return None
3694 else:
3695 return 10*HOUR
3696 dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
3697 self.assertRaises(ValueError, dt.astimezone, tricky_notok())
3698
3699 def test_fromutc(self):
3700 self.assertRaises(TypeError, Eastern.fromutc) # not enough args
3701 now = datetime.utcnow().replace(tzinfo=utc_real)
3702 self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
3703 now = now.replace(tzinfo=Eastern) # insert correct tzinfo
3704 enow = Eastern.fromutc(now) # doesn't blow up
3705 self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
3706 self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
3707 self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
3708
3709 # Always converts UTC to standard time.
3710 class FauxUSTimeZone(USTimeZone):
3711 def fromutc(self, dt):
3712 return dt + self.stdoffset
3713 FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
3714
3715 # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
3716 # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
3717 # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
3718
3719 # Check around DST start.
3720 start = self.dston.replace(hour=4, tzinfo=Eastern)
3721 fstart = start.replace(tzinfo=FEastern)
3722 for wall in 23, 0, 1, 3, 4, 5:
3723 expected = start.replace(hour=wall)
3724 if wall == 23:
3725 expected -= timedelta(days=1)
3726 got = Eastern.fromutc(start)
3727 self.assertEqual(expected, got)
3728
3729 expected = fstart + FEastern.stdoffset
3730 got = FEastern.fromutc(fstart)
3731 self.assertEqual(expected, got)
3732
3733 # Ensure astimezone() calls fromutc() too.
3734 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3735 self.assertEqual(expected, got)
3736
3737 start += HOUR
3738 fstart += HOUR
3739
3740 # Check around DST end.
3741 start = self.dstoff.replace(hour=4, tzinfo=Eastern)
3742 fstart = start.replace(tzinfo=FEastern)
3743 for wall in 0, 1, 1, 2, 3, 4:
3744 expected = start.replace(hour=wall)
3745 got = Eastern.fromutc(start)
3746 self.assertEqual(expected, got)
3747
3748 expected = fstart + FEastern.stdoffset
3749 got = FEastern.fromutc(fstart)
3750 self.assertEqual(expected, got)
3751
3752 # Ensure astimezone() calls fromutc() too.
3753 got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
3754 self.assertEqual(expected, got)
3755
3756 start += HOUR
3757 fstart += HOUR
3758
3759
3760#############################################################################
3761# oddballs
3762
3763class Oddballs(unittest.TestCase):
3764
3765 def test_bug_1028306(self):
3766 # Trying to compare a date to a datetime should act like a mixed-
3767 # type comparison, despite that datetime is a subclass of date.
3768 as_date = date.today()
3769 as_datetime = datetime.combine(as_date, time())
3770 self.assertTrue(as_date != as_datetime)
3771 self.assertTrue(as_datetime != as_date)
Serhiy Storchaka3df4dcc2013-11-17 12:52:33 +02003772 self.assertFalse(as_date == as_datetime)
3773 self.assertFalse(as_datetime == as_date)
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003774 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3775 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3776 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3777 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3778 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3779 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3780 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3781 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3782
3783 # Neverthelss, comparison should work with the base-class (date)
3784 # projection if use of a date method is forced.
3785 self.assertEqual(as_date.__eq__(as_datetime), True)
3786 different_day = (as_date.day + 1) % 20 + 1
3787 as_different = as_datetime.replace(day= different_day)
3788 self.assertEqual(as_date.__eq__(as_different), False)
3789
3790 # And date should compare with other subclasses of date. If a
3791 # subclass wants to stop this, it's up to the subclass to do so.
3792 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3793 self.assertEqual(as_date, date_sc)
3794 self.assertEqual(date_sc, as_date)
3795
3796 # Ditto for datetimes.
3797 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3798 as_date.day, 0, 0, 0)
3799 self.assertEqual(as_datetime, datetime_sc)
3800 self.assertEqual(datetime_sc, as_datetime)
3801
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04003802 def test_extra_attributes(self):
3803 for x in [date.today(),
3804 time(),
3805 datetime.utcnow(),
3806 timedelta(),
3807 tzinfo(),
3808 timezone(timedelta())]:
3809 with self.assertRaises(AttributeError):
3810 x.abc = 1
3811
3812 def test_check_arg_types(self):
Alexander Belopolsky6c7a4182014-09-28 19:11:56 -04003813 class Number:
3814 def __init__(self, value):
3815 self.value = value
3816 def __int__(self):
3817 return self.value
3818
3819 for xx in [decimal.Decimal(10),
3820 decimal.Decimal('10.9'),
3821 Number(10)]:
3822 self.assertEqual(datetime(10, 10, 10, 10, 10, 10, 10),
3823 datetime(xx, xx, xx, xx, xx, xx, xx))
3824
3825 with self.assertRaisesRegex(TypeError, '^an integer is required '
3826 '\(got type str\)$'):
3827 datetime(10, 10, '10')
3828
3829 f10 = Number(10.9)
3830 with self.assertRaisesRegex(TypeError, '^__int__ returned non-int '
3831 '\(type float\)$'):
3832 datetime(10, 10, f10)
3833
3834 class Float(float):
3835 pass
3836 s10 = Float(10.9)
3837 with self.assertRaisesRegex(TypeError, '^integer argument expected, '
3838 'got float$'):
3839 datetime(10, 10, s10)
3840
3841 with self.assertRaises(TypeError):
3842 datetime(10., 10, 10)
3843 with self.assertRaises(TypeError):
3844 datetime(10, 10., 10)
3845 with self.assertRaises(TypeError):
3846 datetime(10, 10, 10.)
3847 with self.assertRaises(TypeError):
3848 datetime(10, 10, 10, 10.)
3849 with self.assertRaises(TypeError):
3850 datetime(10, 10, 10, 10, 10.)
3851 with self.assertRaises(TypeError):
3852 datetime(10, 10, 10, 10, 10, 10.)
3853 with self.assertRaises(TypeError):
3854 datetime(10, 10, 10, 10, 10, 10, 10.)
3855
Alexander Belopolskycf86e362010-07-23 19:25:47 +00003856def test_main():
3857 support.run_unittest(__name__)
3858
3859if __name__ == "__main__":
3860 test_main()