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