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