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