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