Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(2)

Delta Between Two Patch Sets: Lib/test/datetimetester.py

Issue 15873: "datetime" cannot parse ISO 8601 dates and times
Left Patch Set: Created 7 years, 6 months ago
Right Patch Set: Created 3 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « Lib/datetime.py ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 """Test date/time type. 1 """Test date/time type.
2 2
3 See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases 3 See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
4 """ 4 """
5 5 from test.support import is_resource_enabled
6
7 import itertools
8 import bisect
9
10 import copy
11 import decimal
6 import sys 12 import sys
13 import os
7 import pickle 14 import pickle
15 import random
16 import struct
8 import unittest 17 import unittest
18 import sysconfig
19
20 from array import array
9 21
10 from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod 22 from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
11 23
12 from test import support 24 from test import support
13 25
14 import datetime as datetime_module 26 import datetime as datetime_module
15 from datetime import MINYEAR, MAXYEAR 27 from datetime import MINYEAR, MAXYEAR
16 from datetime import timedelta 28 from datetime import timedelta
17 from datetime import tzinfo 29 from datetime import tzinfo
18 from datetime import time 30 from datetime import time
(...skipping 22 matching lines...) Expand all
41 53
42 ############################################################################# 54 #############################################################################
43 # module tests 55 # module tests
44 56
45 class TestModule(unittest.TestCase): 57 class TestModule(unittest.TestCase):
46 58
47 def test_constants(self): 59 def test_constants(self):
48 datetime = datetime_module 60 datetime = datetime_module
49 self.assertEqual(datetime.MINYEAR, 1) 61 self.assertEqual(datetime.MINYEAR, 1)
50 self.assertEqual(datetime.MAXYEAR, 9999) 62 self.assertEqual(datetime.MAXYEAR, 9999)
63
64 def test_name_cleanup(self):
65 if '_Fast' not in str(self):
66 return
67 datetime = datetime_module
68 names = set(name for name in dir(datetime)
69 if not name.startswith('__') and not name.endswith('__'))
70 allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
71 'datetime_CAPI', 'time', 'timedelta', 'timezone',
72 'tzinfo'])
73 self.assertEqual(names - allowed, set([]))
74
75 def test_divide_and_round(self):
76 if '_Fast' in str(self):
77 return
78 dar = datetime_module._divide_and_round
79
80 self.assertEqual(dar(-10, -3), 3)
81 self.assertEqual(dar(5, -2), -2)
82
83 # four cases: (2 signs of a) x (2 signs of b)
84 self.assertEqual(dar(7, 3), 2)
85 self.assertEqual(dar(-7, 3), -2)
86 self.assertEqual(dar(7, -3), -2)
87 self.assertEqual(dar(-7, -3), 2)
88
89 # ties to even - eight cases:
90 # (2 signs of a) x (2 signs of b) x (even / odd quotient)
91 self.assertEqual(dar(10, 4), 2)
92 self.assertEqual(dar(-10, 4), -2)
93 self.assertEqual(dar(10, -4), -2)
94 self.assertEqual(dar(-10, -4), 2)
95
96 self.assertEqual(dar(6, 4), 2)
97 self.assertEqual(dar(-6, 4), -2)
98 self.assertEqual(dar(6, -4), -2)
99 self.assertEqual(dar(-6, -4), 2)
100
51 101
52 ############################################################################# 102 #############################################################################
53 # tzinfo tests 103 # tzinfo tests
54 104
55 class FixedOffset(tzinfo): 105 class FixedOffset(tzinfo):
56 106
57 def __init__(self, offset, name, dstoffset=42): 107 def __init__(self, offset, name, dstoffset=42):
58 if isinstance(offset, int): 108 if isinstance(offset, int):
59 offset = timedelta(minutes=offset) 109 offset = timedelta(minutes=offset)
60 if isinstance(dstoffset, int): 110 if isinstance(dstoffset, int):
61 dstoffset = timedelta(minutes=dstoffset) 111 dstoffset = timedelta(minutes=dstoffset)
62 self.__offset = offset 112 self.__offset = offset
63 self.__name = name 113 self.__name = name
64 self.__dstoffset = dstoffset 114 self.__dstoffset = dstoffset
65 def __repr__(self): 115 def __repr__(self):
66 return self.__name.lower() 116 return self.__name.lower()
67 def utcoffset(self, dt): 117 def utcoffset(self, dt):
68 return self.__offset 118 return self.__offset
69 def tzname(self, dt): 119 def tzname(self, dt):
70 return self.__name 120 return self.__name
71 def dst(self, dt): 121 def dst(self, dt):
72 return self.__dstoffset 122 return self.__dstoffset
73 123
74 class PicklableFixedOffset(FixedOffset): 124 class PicklableFixedOffset(FixedOffset):
75 125
76 def __init__(self, offset=None, name=None, dstoffset=None): 126 def __init__(self, offset=None, name=None, dstoffset=None):
77 FixedOffset.__init__(self, offset, name, dstoffset) 127 FixedOffset.__init__(self, offset, name, dstoffset)
78 128
129 def __getstate__(self):
130 return self.__dict__
131
132 class _TZInfo(tzinfo):
133 def utcoffset(self, datetime_module):
134 return random.random()
135
79 class TestTZInfo(unittest.TestCase): 136 class TestTZInfo(unittest.TestCase):
137
138 def test_refcnt_crash_bug_22044(self):
139 tz1 = _TZInfo()
140 dt1 = datetime(2014, 7, 21, 11, 32, 3, 0, tz1)
141 with self.assertRaises(TypeError):
142 dt1.utcoffset()
80 143
81 def test_non_abstractness(self): 144 def test_non_abstractness(self):
82 # In order to allow subclasses to get pickled, the C implementation 145 # In order to allow subclasses to get pickled, the C implementation
83 # wasn't able to get away with having __init__ raise 146 # wasn't able to get away with having __init__ raise
84 # NotImplementedError. 147 # NotImplementedError.
85 useless = tzinfo() 148 useless = tzinfo()
86 dt = datetime.max 149 dt = datetime.max
87 self.assertRaises(NotImplementedError, useless.tzname, dt) 150 self.assertRaises(NotImplementedError, useless.tzname, dt)
88 self.assertRaises(NotImplementedError, useless.utcoffset, dt) 151 self.assertRaises(NotImplementedError, useless.utcoffset, dt)
89 self.assertRaises(NotImplementedError, useless.dst, dt) 152 self.assertRaises(NotImplementedError, useless.dst, dt)
(...skipping 18 matching lines...) Expand all
108 for dt in datetime.now(), None: 171 for dt in datetime.now(), None:
109 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3)) 172 self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
110 self.assertEqual(fo.tzname(dt), "Three") 173 self.assertEqual(fo.tzname(dt), "Three")
111 self.assertEqual(fo.dst(dt), timedelta(minutes=42)) 174 self.assertEqual(fo.dst(dt), timedelta(minutes=42))
112 175
113 def test_pickling_base(self): 176 def test_pickling_base(self):
114 # There's no point to pickling tzinfo objects on their own (they 177 # There's no point to pickling tzinfo objects on their own (they
115 # carry no data), but they need to be picklable anyway else 178 # carry no data), but they need to be picklable anyway else
116 # concrete subclasses can't be pickled. 179 # concrete subclasses can't be pickled.
117 orig = tzinfo.__new__(tzinfo) 180 orig = tzinfo.__new__(tzinfo)
118 self.assertTrue(type(orig) is tzinfo) 181 self.assertIs(type(orig), tzinfo)
119 for pickler, unpickler, proto in pickle_choices: 182 for pickler, unpickler, proto in pickle_choices:
120 green = pickler.dumps(orig, proto) 183 green = pickler.dumps(orig, proto)
121 derived = unpickler.loads(green) 184 derived = unpickler.loads(green)
122 self.assertTrue(type(derived) is tzinfo) 185 self.assertIs(type(derived), tzinfo)
123 186
124 def test_pickling_subclass(self): 187 def test_pickling_subclass(self):
125 # Make sure we can pickle/unpickle an instance of a subclass. 188 # Make sure we can pickle/unpickle an instance of a subclass.
126 offset = timedelta(minutes=-300) 189 offset = timedelta(minutes=-300)
127 for otype, args in [ 190 for otype, args in [
128 (PicklableFixedOffset, (offset, 'cookie')), 191 (PicklableFixedOffset, (offset, 'cookie')),
129 (timezone, (offset,)), 192 (timezone, (offset,)),
130 (timezone, (offset, "EST"))]: 193 (timezone, (offset, "EST"))]:
131 orig = otype(*args) 194 orig = otype(*args)
132 oname = orig.tzname(None) 195 oname = orig.tzname(None)
133 self.assertIsInstance(orig, tzinfo) 196 self.assertIsInstance(orig, tzinfo)
134 self.assertIs(type(orig), otype) 197 self.assertIs(type(orig), otype)
135 self.assertEqual(orig.utcoffset(None), offset) 198 self.assertEqual(orig.utcoffset(None), offset)
136 self.assertEqual(orig.tzname(None), oname) 199 self.assertEqual(orig.tzname(None), oname)
137 for pickler, unpickler, proto in pickle_choices: 200 for pickler, unpickler, proto in pickle_choices:
138 green = pickler.dumps(orig, proto) 201 green = pickler.dumps(orig, proto)
139 derived = unpickler.loads(green) 202 derived = unpickler.loads(green)
140 self.assertIsInstance(derived, tzinfo) 203 self.assertIsInstance(derived, tzinfo)
141 self.assertIs(type(derived), otype) 204 self.assertIs(type(derived), otype)
142 self.assertEqual(derived.utcoffset(None), offset) 205 self.assertEqual(derived.utcoffset(None), offset)
143 self.assertEqual(derived.tzname(None), oname) 206 self.assertEqual(derived.tzname(None), oname)
144 207
208 def test_issue23600(self):
209 DSTDIFF = DSTOFFSET = timedelta(hours=1)
210
211 class UKSummerTime(tzinfo):
212 """Simple time zone which pretends to always be in summer time, sinc e
213 that's what shows the failure.
214 """
215
216 def utcoffset(self, dt):
217 return DSTOFFSET
218
219 def dst(self, dt):
220 return DSTDIFF
221
222 def tzname(self, dt):
223 return 'UKSummerTime'
224
225 tz = UKSummerTime()
226 u = datetime(2014, 4, 26, 12, 1, tzinfo=tz)
227 t = tz.fromutc(u)
228 self.assertEqual(t - t.utcoffset(), u)
229
230
145 class TestTimeZone(unittest.TestCase): 231 class TestTimeZone(unittest.TestCase):
146 232
147 def setUp(self): 233 def setUp(self):
148 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT') 234 self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
149 self.EST = timezone(-timedelta(hours=5), 'EST') 235 self.EST = timezone(-timedelta(hours=5), 'EST')
150 self.DT = datetime(2010, 1, 1) 236 self.DT = datetime(2010, 1, 1)
151 237
152 def test_str(self): 238 def test_str(self):
153 for tz in [self.ACDT, self.EST, timezone.utc, 239 for tz in [self.ACDT, self.EST, timezone.utc,
154 timezone.min, timezone.max]: 240 timezone.min, timezone.max]:
155 self.assertEqual(str(tz), tz.tzname(None)) 241 self.assertEqual(str(tz), tz.tzname(None))
156 242
157 def test_repr(self): 243 def test_repr(self):
158 datetime = datetime_module 244 datetime = datetime_module
159 for tz in [self.ACDT, self.EST, timezone.utc, 245 for tz in [self.ACDT, self.EST, timezone.utc,
160 timezone.min, timezone.max]: 246 timezone.min, timezone.max]:
161 # test round-trip 247 # test round-trip
162 tzrep = repr(tz) 248 tzrep = repr(tz)
163 self.assertEqual(tz, eval(tzrep)) 249 self.assertEqual(tz, eval(tzrep))
164
165 250
166 def test_class_members(self): 251 def test_class_members(self):
167 limit = timedelta(hours=23, minutes=59) 252 limit = timedelta(hours=23, minutes=59)
168 self.assertEqual(timezone.utc.utcoffset(None), ZERO) 253 self.assertEqual(timezone.utc.utcoffset(None), ZERO)
169 self.assertEqual(timezone.min.utcoffset(None), -limit) 254 self.assertEqual(timezone.min.utcoffset(None), -limit)
170 self.assertEqual(timezone.max.utcoffset(None), limit) 255 self.assertEqual(timezone.max.utcoffset(None), limit)
171 256
172 257
173 def test_constructor(self): 258 def test_constructor(self):
174 self.assertIs(timezone.utc, timezone(timedelta(0))) 259 self.assertIs(timezone.utc, timezone(timedelta(0)))
175 self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC')) 260 self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
176 self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC')) 261 self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
177 # invalid offsets 262 # invalid offsets
178 for invalid in [timedelta(microseconds=1), timedelta(1, 1), 263 for invalid in [timedelta(microseconds=1), timedelta(1, 1),
179 timedelta(seconds=1), timedelta(1), -timedelta(1)]: 264 timedelta(seconds=1), timedelta(1), -timedelta(1)]:
180 self.assertRaises(ValueError, timezone, invalid) 265 self.assertRaises(ValueError, timezone, invalid)
181 self.assertRaises(ValueError, timezone, -invalid) 266 self.assertRaises(ValueError, timezone, -invalid)
182 267
183 with self.assertRaises(TypeError): timezone(None) 268 with self.assertRaises(TypeError): timezone(None)
184 with self.assertRaises(TypeError): timezone(42) 269 with self.assertRaises(TypeError): timezone(42)
185 with self.assertRaises(TypeError): timezone(ZERO, None) 270 with self.assertRaises(TypeError): timezone(ZERO, None)
186 with self.assertRaises(TypeError): timezone(ZERO, 42) 271 with self.assertRaises(TypeError): timezone(ZERO, 42)
187 with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra') 272 with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
188 273
189 # string offset
190 self.assertIs(timezone.utc, timezone('Z'))
191 self.assertIs(timezone.utc, timezone('+0000'))
192 self.assertIs(timezone.utc, timezone('-0000'))
193 self.assertIs(timezone.utc, timezone('+00:00'))
194 self.assertIs(timezone.utc, timezone('-00:00'))
195
196 def test_inheritance(self): 274 def test_inheritance(self):
197 self.assertIsInstance(timezone.utc, tzinfo) 275 self.assertIsInstance(timezone.utc, tzinfo)
198 self.assertIsInstance(self.EST, tzinfo) 276 self.assertIsInstance(self.EST, tzinfo)
199 277
200 def test_utcoffset(self): 278 def test_utcoffset(self):
201 dummy = self.DT 279 dummy = self.DT
202 for h in [0, 1.5, 12]: 280 for h in [0, 1.5, 12]:
203 offset = h * HOUR 281 offset = h * HOUR
204 self.assertEqual(offset, timezone(offset).utcoffset(dummy)) 282 self.assertEqual(offset, timezone(offset).utcoffset(dummy))
205 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy)) 283 self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
206 284
207 with self.assertRaises(TypeError): self.EST.utcoffset('') 285 with self.assertRaises(TypeError): self.EST.utcoffset('')
208 with self.assertRaises(TypeError): self.EST.utcoffset(5) 286 with self.assertRaises(TypeError): self.EST.utcoffset(5)
209 287
210 288
211 def test_dst(self): 289 def test_dst(self):
212 self.assertIsNone(timezone.utc.dst(self.DT)) 290 self.assertIsNone(timezone.utc.dst(self.DT))
213 291
214 with self.assertRaises(TypeError): self.EST.dst('') 292 with self.assertRaises(TypeError): self.EST.dst('')
215 with self.assertRaises(TypeError): self.EST.dst(5) 293 with self.assertRaises(TypeError): self.EST.dst(5)
216 294
217 def test_tzname(self): 295 def test_tzname(self):
218 self.assertEqual('UTC+00:00', timezone(ZERO).tzname(None)) 296 self.assertEqual('UTC', timezone.utc.tzname(None))
297 self.assertEqual('UTC', timezone(ZERO).tzname(None))
219 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None)) 298 self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
220 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None)) 299 self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
221 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(Non e)) 300 self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(Non e))
222 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None)) 301 self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
223 302
224 with self.assertRaises(TypeError): self.EST.tzname('') 303 with self.assertRaises(TypeError): self.EST.tzname('')
225 with self.assertRaises(TypeError): self.EST.tzname(5) 304 with self.assertRaises(TypeError): self.EST.tzname(5)
226 305
227 def test_fromutc(self): 306 def test_fromutc(self):
228 with self.assertRaises(ValueError): 307 with self.assertRaises(ValueError):
229 timezone.utc.fromutc(self.DT) 308 timezone.utc.fromutc(self.DT)
230 with self.assertRaises(TypeError): 309 with self.assertRaises(TypeError):
231 timezone.utc.fromutc('not datetime') 310 timezone.utc.fromutc('not datetime')
232 for tz in [self.EST, self.ACDT, Eastern]: 311 for tz in [self.EST, self.ACDT, Eastern]:
233 utctime = self.DT.replace(tzinfo=tz) 312 utctime = self.DT.replace(tzinfo=tz)
234 local = tz.fromutc(utctime) 313 local = tz.fromutc(utctime)
235 self.assertEqual(local - utctime, tz.utcoffset(local)) 314 self.assertEqual(local - utctime, tz.utcoffset(local))
236 self.assertEqual(local, 315 self.assertEqual(local,
237 self.DT.replace(tzinfo=timezone.utc)) 316 self.DT.replace(tzinfo=timezone.utc))
238 317
239 def test_comparison(self): 318 def test_comparison(self):
240 self.assertNotEqual(timezone(ZERO), timezone(HOUR)) 319 self.assertNotEqual(timezone(ZERO), timezone(HOUR))
241 self.assertEqual(timezone(HOUR), timezone(HOUR)) 320 self.assertEqual(timezone(HOUR), timezone(HOUR))
242 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST')) 321 self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
243 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO) 322 with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
244 self.assertIn(timezone(ZERO), {timezone(ZERO)}) 323 self.assertIn(timezone(ZERO), {timezone(ZERO)})
324 self.assertTrue(timezone(ZERO) != None)
325 self.assertFalse(timezone(ZERO) == None)
245 326
246 def test_aware_datetime(self): 327 def test_aware_datetime(self):
247 # test that timezone instances can be used by datetime 328 # test that timezone instances can be used by datetime
248 t = datetime(1, 1, 1) 329 t = datetime(1, 1, 1)
249 for tz in [timezone.min, timezone.max, timezone.utc]: 330 for tz in [timezone.min, timezone.max, timezone.utc]:
250 self.assertEqual(tz.tzname(t), 331 self.assertEqual(tz.tzname(t),
251 t.replace(tzinfo=tz).tzname()) 332 t.replace(tzinfo=tz).tzname())
252 self.assertEqual(tz.utcoffset(t), 333 self.assertEqual(tz.utcoffset(t),
253 t.replace(tzinfo=tz).utcoffset()) 334 t.replace(tzinfo=tz).utcoffset())
254 self.assertEqual(tz.dst(t), 335 self.assertEqual(tz.dst(t),
255 t.replace(tzinfo=tz).dst()) 336 t.replace(tzinfo=tz).dst())
256 337
338 def test_pickle(self):
339 for tz in self.ACDT, self.EST, timezone.min, timezone.max:
340 for pickler, unpickler, proto in pickle_choices:
341 tz_copy = unpickler.loads(pickler.dumps(tz, proto))
342 self.assertEqual(tz_copy, tz)
343 tz = timezone.utc
344 for pickler, unpickler, proto in pickle_choices:
345 tz_copy = unpickler.loads(pickler.dumps(tz, proto))
346 self.assertIs(tz_copy, tz)
347
348 def test_copy(self):
349 for tz in self.ACDT, self.EST, timezone.min, timezone.max:
350 tz_copy = copy.copy(tz)
351 self.assertEqual(tz_copy, tz)
352 tz = timezone.utc
353 tz_copy = copy.copy(tz)
354 self.assertIs(tz_copy, tz)
355
356 def test_deepcopy(self):
357 for tz in self.ACDT, self.EST, timezone.min, timezone.max:
358 tz_copy = copy.deepcopy(tz)
359 self.assertEqual(tz_copy, tz)
360 tz = timezone.utc
361 tz_copy = copy.deepcopy(tz)
362 self.assertIs(tz_copy, tz)
363
364
257 ############################################################################# 365 #############################################################################
258 # Base clase for testing a particular aspect of timedelta, time, date and 366 # Base class for testing a particular aspect of timedelta, time, date and
259 # datetime comparisons. 367 # datetime comparisons.
260 368
261 class HarmlessMixedComparison: 369 class HarmlessMixedComparison:
262 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons. 370 # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
263 371
264 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a 372 # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
265 # legit constructor. 373 # legit constructor.
266 374
267 def test_harmless_mixed_comparison(self): 375 def test_harmless_mixed_comparison(self):
268 me = self.theclass(1, 1, 1) 376 me = self.theclass(1, 1, 1)
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 477
370 # Multiplication by float 478 # Multiplication by float
371 us = td(microseconds=1) 479 us = td(microseconds=1)
372 eq((3*us) * 0.5, 2*us) 480 eq((3*us) * 0.5, 2*us)
373 eq((5*us) * 0.5, 2*us) 481 eq((5*us) * 0.5, 2*us)
374 eq(0.5 * (3*us), 2*us) 482 eq(0.5 * (3*us), 2*us)
375 eq(0.5 * (5*us), 2*us) 483 eq(0.5 * (5*us), 2*us)
376 eq((-3*us) * 0.5, -2*us) 484 eq((-3*us) * 0.5, -2*us)
377 eq((-5*us) * 0.5, -2*us) 485 eq((-5*us) * 0.5, -2*us)
378 486
487 # Issue #23521
488 eq(td(seconds=1) * 0.123456, td(microseconds=123456))
489 eq(td(seconds=1) * 0.6112295, td(microseconds=611229))
490
379 # Division by int and float 491 # Division by int and float
380 eq((3*us) / 2, 2*us) 492 eq((3*us) / 2, 2*us)
381 eq((5*us) / 2, 2*us) 493 eq((5*us) / 2, 2*us)
382 eq((-3*us) / 2.0, -2*us) 494 eq((-3*us) / 2.0, -2*us)
383 eq((-5*us) / 2.0, -2*us) 495 eq((-5*us) / 2.0, -2*us)
384 eq((3*us) / -2, -2*us) 496 eq((3*us) / -2, -2*us)
385 eq((5*us) / -2, -2*us) 497 eq((5*us) / -2, -2*us)
386 eq((3*us) / -2.0, -2*us) 498 eq((3*us) / -2.0, -2*us)
387 eq((5*us) / -2.0, -2*us) 499 eq((5*us) / -2.0, -2*us)
388 for i in range(-10, 10): 500 for i in range(-10, 10):
389 eq((i*us/3)//us, round(i/3)) 501 eq((i*us/3)//us, round(i/3))
390 for i in range(-10, 10): 502 for i in range(-10, 10):
391 eq((i*us/-3)//us, round(i/-3)) 503 eq((i*us/-3)//us, round(i/-3))
392 504
505 # Issue #23521
506 eq(td(seconds=1) / (1 / 0.6112295), td(microseconds=611229))
507
393 # Issue #11576 508 # Issue #11576
394 eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998), 509 eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
395 td(0, 0, 1)) 510 td(0, 0, 1))
396 eq(td(999999999, 1, 1) - td(999999999, 1, 0), 511 eq(td(999999999, 1, 1) - td(999999999, 1, 0),
397 td(0, 0, 1)) 512 td(0, 0, 1))
398 513
399 def test_disallowed_computations(self): 514 def test_disallowed_computations(self):
400 a = timedelta(42) 515 a = timedelta(42)
401 516
402 # Add/sub ints or floats should be illegal 517 # Add/sub ints or floats should be illegal
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
477 green = pickler.dumps(orig, proto) 592 green = pickler.dumps(orig, proto)
478 derived = unpickler.loads(green) 593 derived = unpickler.loads(green)
479 self.assertEqual(orig, derived) 594 self.assertEqual(orig, derived)
480 595
481 def test_compare(self): 596 def test_compare(self):
482 t1 = timedelta(2, 3, 4) 597 t1 = timedelta(2, 3, 4)
483 t2 = timedelta(2, 3, 4) 598 t2 = timedelta(2, 3, 4)
484 self.assertEqual(t1, t2) 599 self.assertEqual(t1, t2)
485 self.assertTrue(t1 <= t2) 600 self.assertTrue(t1 <= t2)
486 self.assertTrue(t1 >= t2) 601 self.assertTrue(t1 >= t2)
487 self.assertTrue(not t1 != t2) 602 self.assertFalse(t1 != t2)
488 self.assertTrue(not t1 < t2) 603 self.assertFalse(t1 < t2)
489 self.assertTrue(not t1 > t2) 604 self.assertFalse(t1 > t2)
490 605
491 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5): 606 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
492 t2 = timedelta(*args) # this is larger than t1 607 t2 = timedelta(*args) # this is larger than t1
493 self.assertTrue(t1 < t2) 608 self.assertTrue(t1 < t2)
494 self.assertTrue(t2 > t1) 609 self.assertTrue(t2 > t1)
495 self.assertTrue(t1 <= t2) 610 self.assertTrue(t1 <= t2)
496 self.assertTrue(t2 >= t1) 611 self.assertTrue(t2 >= t1)
497 self.assertTrue(t1 != t2) 612 self.assertTrue(t1 != t2)
498 self.assertTrue(t2 != t1) 613 self.assertTrue(t2 != t1)
499 self.assertTrue(not t1 == t2) 614 self.assertFalse(t1 == t2)
500 self.assertTrue(not t2 == t1) 615 self.assertFalse(t2 == t1)
501 self.assertTrue(not t1 > t2) 616 self.assertFalse(t1 > t2)
502 self.assertTrue(not t2 < t1) 617 self.assertFalse(t2 < t1)
503 self.assertTrue(not t1 >= t2) 618 self.assertFalse(t1 >= t2)
504 self.assertTrue(not t2 <= t1) 619 self.assertFalse(t2 <= t1)
505 620
506 for badarg in OTHERSTUFF: 621 for badarg in OTHERSTUFF:
507 self.assertEqual(t1 == badarg, False) 622 self.assertEqual(t1 == badarg, False)
508 self.assertEqual(t1 != badarg, True) 623 self.assertEqual(t1 != badarg, True)
509 self.assertEqual(badarg == t1, False) 624 self.assertEqual(badarg == t1, False)
510 self.assertEqual(badarg != t1, True) 625 self.assertEqual(badarg != t1, True)
511 626
512 self.assertRaises(TypeError, lambda: t1 <= badarg) 627 self.assertRaises(TypeError, lambda: t1 <= badarg)
513 self.assertRaises(TypeError, lambda: t1 < badarg) 628 self.assertRaises(TypeError, lambda: t1 < badarg)
514 self.assertRaises(TypeError, lambda: t1 > badarg) 629 self.assertRaises(TypeError, lambda: t1 > badarg)
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
603 self.assertRaises(OverflowError, day.__mul__, INF) 718 self.assertRaises(OverflowError, day.__mul__, INF)
604 self.assertRaises(OverflowError, day.__mul__, -INF) 719 self.assertRaises(OverflowError, day.__mul__, -INF)
605 720
606 def test_microsecond_rounding(self): 721 def test_microsecond_rounding(self):
607 td = timedelta 722 td = timedelta
608 eq = self.assertEqual 723 eq = self.assertEqual
609 724
610 # Single-field rounding. 725 # Single-field rounding.
611 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0 726 eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
612 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0 727 eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
728 eq(td(milliseconds=0.5/1000), td(microseconds=0))
729 eq(td(milliseconds=-0.5/1000), td(microseconds=-0))
613 eq(td(milliseconds=0.6/1000), td(microseconds=1)) 730 eq(td(milliseconds=0.6/1000), td(microseconds=1))
614 eq(td(milliseconds=-0.6/1000), td(microseconds=-1)) 731 eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
732 eq(td(milliseconds=1.5/1000), td(microseconds=2))
733 eq(td(milliseconds=-1.5/1000), td(microseconds=-2))
734 eq(td(seconds=0.5/10**6), td(microseconds=0))
735 eq(td(seconds=-0.5/10**6), td(microseconds=-0))
736 eq(td(seconds=1/2**7), td(microseconds=7812))
737 eq(td(seconds=-1/2**7), td(microseconds=-7812))
615 738
616 # Rounding due to contributions from more than one field. 739 # Rounding due to contributions from more than one field.
617 us_per_hour = 3600e6 740 us_per_hour = 3600e6
618 us_per_day = us_per_hour * 24 741 us_per_day = us_per_hour * 24
619 eq(td(days=.4/us_per_day), td(0)) 742 eq(td(days=.4/us_per_day), td(0))
620 eq(td(hours=.2/us_per_hour), td(0)) 743 eq(td(hours=.2/us_per_hour), td(0))
621 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1)) 744 eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
622 745
623 eq(td(days=-.4/us_per_day), td(0)) 746 eq(td(days=-.4/us_per_day), td(0))
624 eq(td(hours=-.2/us_per_hour), td(0)) 747 eq(td(hours=-.2/us_per_hour), td(0))
625 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1)) 748 eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
626 749
750 # Test for a patch in Issue 8860
751 eq(td(microseconds=0.5), 0.5*td(microseconds=1.0))
752 eq(td(microseconds=0.5)//td.resolution, 0.5*td.resolution//td.resolution )
753
627 def test_massive_normalization(self): 754 def test_massive_normalization(self):
628 td = timedelta(microseconds=-1) 755 td = timedelta(microseconds=-1)
629 self.assertEqual((td.days, td.seconds, td.microseconds), 756 self.assertEqual((td.days, td.seconds, td.microseconds),
630 (-1, 24*3600-1, 999999)) 757 (-1, 24*3600-1, 999999))
631 758
632 def test_bool(self): 759 def test_bool(self):
633 self.assertTrue(timedelta(1)) 760 self.assertTrue(timedelta(1))
634 self.assertTrue(timedelta(0, 1)) 761 self.assertTrue(timedelta(0, 1))
635 self.assertTrue(timedelta(0, 0, 1)) 762 self.assertTrue(timedelta(0, 0, 1))
636 self.assertTrue(timedelta(microseconds=1)) 763 self.assertTrue(timedelta(microseconds=1))
637 self.assertTrue(not timedelta(0)) 764 self.assertFalse(timedelta(0))
638 765
639 def test_subclass_timedelta(self): 766 def test_subclass_timedelta(self):
640 767
641 class T(timedelta): 768 class T(timedelta):
642 @staticmethod 769 @staticmethod
643 def from_td(td): 770 def from_td(td):
644 return T(td.days, td.seconds, td.microseconds) 771 return T(td.days, td.seconds, td.microseconds)
645 772
646 def as_hours(self): 773 def as_hours(self):
647 sum = (self.days * 24 + 774 sum = (self.days * 24 +
648 self.seconds / 3600.0 + 775 self.seconds / 3600.0 +
649 self.microseconds / 3600e6) 776 self.microseconds / 3600e6)
650 return round(sum) 777 return round(sum)
651 778
652 t1 = T(days=1) 779 t1 = T(days=1)
653 self.assertTrue(type(t1) is T) 780 self.assertIs(type(t1), T)
654 self.assertEqual(t1.as_hours(), 24) 781 self.assertEqual(t1.as_hours(), 24)
655 782
656 t2 = T(days=-1, seconds=-3600) 783 t2 = T(days=-1, seconds=-3600)
657 self.assertTrue(type(t2) is T) 784 self.assertIs(type(t2), T)
658 self.assertEqual(t2.as_hours(), -25) 785 self.assertEqual(t2.as_hours(), -25)
659 786
660 t3 = t1 + t2 787 t3 = t1 + t2
661 self.assertTrue(type(t3) is timedelta) 788 self.assertIs(type(t3), timedelta)
662 t4 = T.from_td(t3) 789 t4 = T.from_td(t3)
663 self.assertTrue(type(t4) is T) 790 self.assertIs(type(t4), T)
664 self.assertEqual(t3.days, t4.days) 791 self.assertEqual(t3.days, t4.days)
665 self.assertEqual(t3.seconds, t4.seconds) 792 self.assertEqual(t3.seconds, t4.seconds)
666 self.assertEqual(t3.microseconds, t4.microseconds) 793 self.assertEqual(t3.microseconds, t4.microseconds)
667 self.assertEqual(str(t3), str(t4)) 794 self.assertEqual(str(t3), str(t4))
668 self.assertEqual(t4.as_hours(), -1) 795 self.assertEqual(t4.as_hours(), -1)
669 796
670 def test_division(self): 797 def test_division(self):
671 t = timedelta(hours=1, minutes=24, seconds=19) 798 t = timedelta(hours=1, minutes=24, seconds=19)
672 second = timedelta(seconds=1) 799 second = timedelta(seconds=1)
673 self.assertEqual(t / second, 5059.0) 800 self.assertEqual(t / second, 5059.0)
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
756 self.assertEqual(dt2, dt - days) 883 self.assertEqual(dt2, dt - days)
757 884
758 class SubclassDate(date): 885 class SubclassDate(date):
759 sub_var = 1 886 sub_var = 1
760 887
761 class TestDate(HarmlessMixedComparison, unittest.TestCase): 888 class TestDate(HarmlessMixedComparison, unittest.TestCase):
762 # Tests here should pass for both dates and datetimes, except for a 889 # Tests here should pass for both dates and datetimes, except for a
763 # few tests that TestDateTime overrides. 890 # few tests that TestDateTime overrides.
764 891
765 theclass = date 892 theclass = date
766 def test_iso_constructor(self):
767 dt = self.theclass(2002, 3, 1)
768 self.assertEqual(self.theclass(dt.isoformat()), dt)
769 dt = self.theclass(1, 1, 1)
770 self.assertEqual(self.theclass(dt.isoformat()), dt)
771 dt = self.theclass(9999, 9, 9)
772 self.assertEqual(self.theclass(dt.isoformat()), dt)
773 893
774 def test_basic_attributes(self): 894 def test_basic_attributes(self):
775 dt = self.theclass(2002, 3, 1) 895 dt = self.theclass(2002, 3, 1)
776 self.assertEqual(dt.year, 2002) 896 self.assertEqual(dt.year, 2002)
777 self.assertEqual(dt.month, 3) 897 self.assertEqual(dt.month, 3)
778 self.assertEqual(dt.day, 1) 898 self.assertEqual(dt.day, 1)
779 899
780 def test_roundtrip(self): 900 def test_roundtrip(self):
781 for dt in (self.theclass(1, 2, 3), 901 for dt in (self.theclass(1, 2, 3),
782 self.theclass.today()): 902 self.theclass.today()):
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
978 import time 1098 import time
979 1099
980 # Try an arbitrary fixed value. 1100 # Try an arbitrary fixed value.
981 year, month, day = 1999, 9, 19 1101 year, month, day = 1999, 9, 19
982 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1)) 1102 ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
983 d = self.theclass.fromtimestamp(ts) 1103 d = self.theclass.fromtimestamp(ts)
984 self.assertEqual(d.year, year) 1104 self.assertEqual(d.year, year)
985 self.assertEqual(d.month, month) 1105 self.assertEqual(d.month, month)
986 self.assertEqual(d.day, day) 1106 self.assertEqual(d.day, day)
987 1107
1108 def test_fromisoformat(self):
1109 self.assertEqual(self.theclass.fromisoformat('2014-12-31'),
1110 self.theclass(2014, 12, 31))
1111 self.assertEqual(self.theclass.fromisoformat('4095-07-31'),
1112 self.theclass(4095, 7, 31))
1113
1114 with self.assertRaises(ValueError):
1115 self.theclass.fromisoformat('2014-12-011')
1116 with self.assertRaises(ValueError):
1117 self.theclass.fromisoformat('20141211')
1118 with self.assertRaises(ValueError):
1119 self.theclass.fromisoformat('043-12-01')
1120
988 def test_insane_fromtimestamp(self): 1121 def test_insane_fromtimestamp(self):
989 # It's possible that some platform maps time_t to double, 1122 # It's possible that some platform maps time_t to double,
990 # and that this test will fail there. This test should 1123 # and that this test will fail there. This test should
991 # exempt such platforms (provided they return reasonable 1124 # exempt such platforms (provided they return reasonable
992 # results!). 1125 # results!).
993 for insane in -1e200, 1e200: 1126 for insane in -1e200, 1e200:
994 self.assertRaises(OverflowError, self.theclass.fromtimestamp, 1127 self.assertRaises(OverflowError, self.theclass.fromtimestamp,
995 insane) 1128 insane)
996 1129
997 def test_today(self): 1130 def test_today(self):
(...skipping 14 matching lines...) Expand all
1012 # never get the same value twice. 1145 # never get the same value twice.
1013 # 3. The platform time() has poor resolution, and we just 1146 # 3. The platform time() has poor resolution, and we just
1014 # happened to call today() right before a resolution quantum 1147 # happened to call today() right before a resolution quantum
1015 # boundary. 1148 # boundary.
1016 # 4. The system clock got fiddled between calls. 1149 # 4. The system clock got fiddled between calls.
1017 # In any case, wait a little while and try again. 1150 # In any case, wait a little while and try again.
1018 time.sleep(0.1) 1151 time.sleep(0.1)
1019 1152
1020 # It worked or it didn't. If it didn't, assume it's reason #2, and 1153 # It worked or it didn't. If it didn't, assume it's reason #2, and
1021 # let the test pass if they're within half a second of each other. 1154 # let the test pass if they're within half a second of each other.
1022 self.assertTrue(today == todayagain or 1155 if today != todayagain:
1023 abs(todayagain - today) < timedelta(seconds=0.5)) 1156 self.assertAlmostEqual(todayagain, today,
1157 delta=timedelta(seconds=0.5))
1024 1158
1025 def test_weekday(self): 1159 def test_weekday(self):
1026 for i in range(7): 1160 for i in range(7):
1027 # March 4, 2002 is a Monday 1161 # March 4, 2002 is a Monday
1028 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i) 1162 self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
1029 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1) 1163 self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
1030 # January 2, 1956 is a Monday 1164 # January 2, 1956 is a Monday
1031 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i) 1165 self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
1032 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1) 1166 self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
1033 1167
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
1109 1243
1110 # A naive object replaces %z and %Z w/ empty strings. 1244 # A naive object replaces %z and %Z w/ empty strings.
1111 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") 1245 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
1112 1246
1113 #make sure that invalid format specifiers are handled correctly 1247 #make sure that invalid format specifiers are handled correctly
1114 #self.assertRaises(ValueError, t.strftime, "%e") 1248 #self.assertRaises(ValueError, t.strftime, "%e")
1115 #self.assertRaises(ValueError, t.strftime, "%") 1249 #self.assertRaises(ValueError, t.strftime, "%")
1116 #self.assertRaises(ValueError, t.strftime, "%#") 1250 #self.assertRaises(ValueError, t.strftime, "%#")
1117 1251
1118 #oh well, some systems just ignore those invalid ones. 1252 #oh well, some systems just ignore those invalid ones.
1119 #at least, excercise them to make sure that no crashes 1253 #at least, exercise them to make sure that no crashes
1120 #are generated 1254 #are generated
1121 for f in ["%e", "%", "%#"]: 1255 for f in ["%e", "%", "%#"]:
1122 try: 1256 try:
1123 t.strftime(f) 1257 t.strftime(f)
1124 except ValueError: 1258 except ValueError:
1125 pass 1259 pass
1126 1260
1127 #check that this standard extension works 1261 #check that this standard extension works
1128 t.strftime("%f") 1262 t.strftime("%f")
1129 1263
1130
1131 def test_format(self): 1264 def test_format(self):
1132 dt = self.theclass(2007, 9, 10) 1265 dt = self.theclass(2007, 9, 10)
1133 self.assertEqual(dt.__format__(''), str(dt)) 1266 self.assertEqual(dt.__format__(''), str(dt))
1267
1268 with self.assertRaisesRegex(TypeError, 'must be str, not int'):
1269 dt.__format__(123)
1134 1270
1135 # check that a derived class's __str__() gets called 1271 # check that a derived class's __str__() gets called
1136 class A(self.theclass): 1272 class A(self.theclass):
1137 def __str__(self): 1273 def __str__(self):
1138 return 'A' 1274 return 'A'
1139 a = A(2007, 9, 10) 1275 a = A(2007, 9, 10)
1140 self.assertEqual(a.__format__(''), 'A') 1276 self.assertEqual(a.__format__(''), 'A')
1141 1277
1142 # check that a derived class's strftime gets called 1278 # check that a derived class's strftime gets called
1143 class B(self.theclass): 1279 class B(self.theclass):
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
1207 green = pickler.dumps(orig, proto) 1343 green = pickler.dumps(orig, proto)
1208 derived = unpickler.loads(green) 1344 derived = unpickler.loads(green)
1209 self.assertEqual(orig, derived) 1345 self.assertEqual(orig, derived)
1210 1346
1211 def test_compare(self): 1347 def test_compare(self):
1212 t1 = self.theclass(2, 3, 4) 1348 t1 = self.theclass(2, 3, 4)
1213 t2 = self.theclass(2, 3, 4) 1349 t2 = self.theclass(2, 3, 4)
1214 self.assertEqual(t1, t2) 1350 self.assertEqual(t1, t2)
1215 self.assertTrue(t1 <= t2) 1351 self.assertTrue(t1 <= t2)
1216 self.assertTrue(t1 >= t2) 1352 self.assertTrue(t1 >= t2)
1217 self.assertTrue(not t1 != t2) 1353 self.assertFalse(t1 != t2)
1218 self.assertTrue(not t1 < t2) 1354 self.assertFalse(t1 < t2)
1219 self.assertTrue(not t1 > t2) 1355 self.assertFalse(t1 > t2)
1220 1356
1221 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5): 1357 for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
1222 t2 = self.theclass(*args) # this is larger than t1 1358 t2 = self.theclass(*args) # this is larger than t1
1223 self.assertTrue(t1 < t2) 1359 self.assertTrue(t1 < t2)
1224 self.assertTrue(t2 > t1) 1360 self.assertTrue(t2 > t1)
1225 self.assertTrue(t1 <= t2) 1361 self.assertTrue(t1 <= t2)
1226 self.assertTrue(t2 >= t1) 1362 self.assertTrue(t2 >= t1)
1227 self.assertTrue(t1 != t2) 1363 self.assertTrue(t1 != t2)
1228 self.assertTrue(t2 != t1) 1364 self.assertTrue(t2 != t1)
1229 self.assertTrue(not t1 == t2) 1365 self.assertFalse(t1 == t2)
1230 self.assertTrue(not t2 == t1) 1366 self.assertFalse(t2 == t1)
1231 self.assertTrue(not t1 > t2) 1367 self.assertFalse(t1 > t2)
1232 self.assertTrue(not t2 < t1) 1368 self.assertFalse(t2 < t1)
1233 self.assertTrue(not t1 >= t2) 1369 self.assertFalse(t1 >= t2)
1234 self.assertTrue(not t2 <= t1) 1370 self.assertFalse(t2 <= t1)
1235 1371
1236 for badarg in OTHERSTUFF: 1372 for badarg in OTHERSTUFF:
1237 self.assertEqual(t1 == badarg, False) 1373 self.assertEqual(t1 == badarg, False)
1238 self.assertEqual(t1 != badarg, True) 1374 self.assertEqual(t1 != badarg, True)
1239 self.assertEqual(badarg == t1, False) 1375 self.assertEqual(badarg == t1, False)
1240 self.assertEqual(badarg != t1, True) 1376 self.assertEqual(badarg != t1, True)
1241 1377
1242 self.assertRaises(TypeError, lambda: t1 < badarg) 1378 self.assertRaises(TypeError, lambda: t1 < badarg)
1243 self.assertRaises(TypeError, lambda: t1 > badarg) 1379 self.assertRaises(TypeError, lambda: t1 > badarg)
1244 self.assertRaises(TypeError, lambda: t1 >= badarg) 1380 self.assertRaises(TypeError, lambda: t1 >= badarg)
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1276 # However, if the other class explicitly defines ordering 1412 # However, if the other class explicitly defines ordering
1277 # relative to our class, it is allowed to do so 1413 # relative to our class, it is allowed to do so
1278 1414
1279 class LargerThanAnything: 1415 class LargerThanAnything:
1280 def __lt__(self, other): 1416 def __lt__(self, other):
1281 return False 1417 return False
1282 def __le__(self, other): 1418 def __le__(self, other):
1283 return isinstance(other, LargerThanAnything) 1419 return isinstance(other, LargerThanAnything)
1284 def __eq__(self, other): 1420 def __eq__(self, other):
1285 return isinstance(other, LargerThanAnything) 1421 return isinstance(other, LargerThanAnything)
1286 def __ne__(self, other):
1287 return not isinstance(other, LargerThanAnything)
1288 def __gt__(self, other): 1422 def __gt__(self, other):
1289 return not isinstance(other, LargerThanAnything) 1423 return not isinstance(other, LargerThanAnything)
1290 def __ge__(self, other): 1424 def __ge__(self, other):
1291 return True 1425 return True
1292 1426
1293 their = LargerThanAnything() 1427 their = LargerThanAnything()
1294 self.assertEqual(our == their, False) 1428 self.assertEqual(our == their, False)
1295 self.assertEqual(their == our, False) 1429 self.assertEqual(their == our, False)
1296 self.assertEqual(our != their, True) 1430 self.assertEqual(our != their, True)
1297 self.assertEqual(their != our, True) 1431 self.assertEqual(their != our, True)
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
1380 # checks in the constructor. This can create insane objects. 1514 # checks in the constructor. This can create insane objects.
1381 # The constructor doesn't want to burn the time to validate all 1515 # The constructor doesn't want to burn the time to validate all
1382 # fields, but does check the month field. This stops, e.g., 1516 # fields, but does check the month field. This stops, e.g.,
1383 # datetime.datetime('1995-03-25') from yielding an insane object. 1517 # datetime.datetime('1995-03-25') from yielding an insane object.
1384 base = b'1995-03-25' 1518 base = b'1995-03-25'
1385 if not issubclass(self.theclass, datetime): 1519 if not issubclass(self.theclass, datetime):
1386 base = base[:4] 1520 base = base[:4]
1387 for month_byte in b'9', b'\0', b'\r', b'\xff': 1521 for month_byte in b'9', b'\0', b'\r', b'\xff':
1388 self.assertRaises(TypeError, self.theclass, 1522 self.assertRaises(TypeError, self.theclass,
1389 base[:2] + month_byte + base[3:]) 1523 base[:2] + month_byte + base[3:])
1390 # Good bytes, but bad tzinfo: 1524 if issubclass(self.theclass, datetime):
1391 self.assertRaises(TypeError, self.theclass, 1525 # Good bytes, but bad tzinfo:
1392 bytes([1] * len(base)), 'EST') 1526 with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
1527 self.theclass(bytes([1] * len(base)), 'EST')
1393 1528
1394 for ord_byte in range(1, 13): 1529 for ord_byte in range(1, 13):
1395 # This shouldn't blow up because of the month byte alone. If 1530 # This shouldn't blow up because of the month byte alone. If
1396 # the implementation changes to do more-careful checking, it may 1531 # the implementation changes to do more-careful checking, it may
1397 # blow up because other fields are insane. 1532 # blow up because other fields are insane.
1398 self.theclass(base[:2] + bytes([ord_byte]) + base[3:]) 1533 self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
1399 1534
1400 ############################################################################# 1535 #############################################################################
1401 # datetime tests 1536 # datetime tests
1402 1537
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
1439 dt2 = eval(s) 1574 dt2 = eval(s)
1440 self.assertEqual(dt, dt2) 1575 self.assertEqual(dt, dt2)
1441 1576
1442 # Verify identity via reconstructing from pieces. 1577 # Verify identity via reconstructing from pieces.
1443 dt2 = self.theclass(dt.year, dt.month, dt.day, 1578 dt2 = self.theclass(dt.year, dt.month, dt.day,
1444 dt.hour, dt.minute, dt.second, 1579 dt.hour, dt.minute, dt.second,
1445 dt.microsecond) 1580 dt.microsecond)
1446 self.assertEqual(dt, dt2) 1581 self.assertEqual(dt, dt2)
1447 1582
1448 def test_isoformat(self): 1583 def test_isoformat(self):
1449 t = self.theclass(2, 3, 2, 4, 5, 1, 123) 1584 t = self.theclass(1, 2, 3, 4, 5, 1, 123)
1450 self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123") 1585 self.assertEqual(t.isoformat(), "0001-02-03T04:05:01.000123")
1451 self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123") 1586 self.assertEqual(t.isoformat('T'), "0001-02-03T04:05:01.000123")
1452 self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123") 1587 self.assertEqual(t.isoformat(' '), "0001-02-03 04:05:01.000123")
1453 self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123") 1588 self.assertEqual(t.isoformat('\x00'), "0001-02-03\x0004:05:01.000123")
1589 self.assertEqual(t.isoformat(timespec='hours'), "0001-02-03T04")
1590 self.assertEqual(t.isoformat(timespec='minutes'), "0001-02-03T04:05")
1591 self.assertEqual(t.isoformat(timespec='seconds'), "0001-02-03T04:05:01")
1592 self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05 :01.000")
1593 self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05 :01.000123")
1594 self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01.0001 23")
1595 self.assertEqual(t.isoformat(sep=' ', timespec='minutes'), "0001-02-03 0 4:05")
1596 self.assertRaises(ValueError, t.isoformat, timespec='foo')
1454 # str is ISO format with the separator forced to a blank. 1597 # str is ISO format with the separator forced to a blank.
1455 self.assertEqual(str(t), "0002-03-02 04:05:01.000123") 1598 self.assertEqual(str(t), "0001-02-03 04:05:01.000123")
1599
1600 t = self.theclass(1, 2, 3, 4, 5, 1, 999500, tzinfo=timezone.utc)
1601 self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05 :01.999+00:00")
1602
1603 t = self.theclass(1, 2, 3, 4, 5, 1, 999500)
1604 self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05 :01.999")
1605
1606 t = self.theclass(1, 2, 3, 4, 5, 1)
1607 self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01")
1608 self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05 :01.000")
1609 self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05 :01.000000")
1456 1610
1457 t = self.theclass(2, 3, 2) 1611 t = self.theclass(2, 3, 2)
1458 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00") 1612 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
1459 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00") 1613 self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
1460 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00") 1614 self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
1461 # str is ISO format with the separator forced to a blank. 1615 # str is ISO format with the separator forced to a blank.
1462 self.assertEqual(str(t), "0002-03-02 00:00:00") 1616 self.assertEqual(str(t), "0002-03-02 00:00:00")
1617 # ISO format with timezone
1618 tz = FixedOffset(timedelta(seconds=16), 'XXX')
1619 t = self.theclass(2, 3, 2, tzinfo=tz)
1620 self.assertEqual(t.isoformat(), "0002-03-02T00:00:00+00:00:16")
1463 1621
1464 def test_format(self): 1622 def test_format(self):
1465 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123) 1623 dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1466 self.assertEqual(dt.__format__(''), str(dt)) 1624 self.assertEqual(dt.__format__(''), str(dt))
1625
1626 with self.assertRaisesRegex(TypeError, 'must be str, not int'):
1627 dt.__format__(123)
1467 1628
1468 # check that a derived class's __str__() gets called 1629 # check that a derived class's __str__() gets called
1469 class A(self.theclass): 1630 class A(self.theclass):
1470 def __str__(self): 1631 def __str__(self):
1471 return 'A' 1632 return 'A'
1472 a = A(2007, 9, 10, 4, 5, 1, 123) 1633 a = A(2007, 9, 10, 4, 5, 1, 123)
1473 self.assertEqual(a.__format__(''), 'A') 1634 self.assertEqual(a.__format__(''), 'A')
1474 1635
1475 # check that a derived class's strftime gets called 1636 # check that a derived class's strftime gets called
1476 class B(self.theclass): 1637 class B(self.theclass):
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
1569 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1) 1730 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
1570 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60) 1731 self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
1571 # bad microseconds 1732 # bad microseconds
1572 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception 1733 self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
1573 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception 1734 self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
1574 self.assertRaises(ValueError, self.theclass, 1735 self.assertRaises(ValueError, self.theclass,
1575 2000, 1, 31, 23, 59, 59, -1) 1736 2000, 1, 31, 23, 59, 59, -1)
1576 self.assertRaises(ValueError, self.theclass, 1737 self.assertRaises(ValueError, self.theclass,
1577 2000, 1, 31, 23, 59, 59, 1738 2000, 1, 31, 23, 59, 59,
1578 1000000) 1739 1000000)
1740 # Positional fold:
1741 self.assertRaises(TypeError, self.theclass,
1742 2000, 1, 31, 23, 59, 59, 0, None, 1)
1579 1743
1580 def test_hash_equality(self): 1744 def test_hash_equality(self):
1581 d = self.theclass(2000, 12, 31, 23, 30, 17) 1745 d = self.theclass(2000, 12, 31, 23, 30, 17)
1582 e = self.theclass(2000, 12, 31, 23, 30, 17) 1746 e = self.theclass(2000, 12, 31, 23, 30, 17)
1583 self.assertEqual(d, e) 1747 self.assertEqual(d, e)
1584 self.assertEqual(hash(d), hash(e)) 1748 self.assertEqual(hash(d), hash(e))
1585 1749
1586 dic = {d: 1} 1750 dic = {d: 1}
1587 dic[e] = 2 1751 dic[e] = 2
1588 self.assertEqual(len(dic), 1) 1752 self.assertEqual(len(dic), 1)
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
1671 def test_pickling(self): 1835 def test_pickling(self):
1672 args = 6, 7, 23, 20, 59, 1, 64**2 1836 args = 6, 7, 23, 20, 59, 1, 64**2
1673 orig = self.theclass(*args) 1837 orig = self.theclass(*args)
1674 for pickler, unpickler, proto in pickle_choices: 1838 for pickler, unpickler, proto in pickle_choices:
1675 green = pickler.dumps(orig, proto) 1839 green = pickler.dumps(orig, proto)
1676 derived = unpickler.loads(green) 1840 derived = unpickler.loads(green)
1677 self.assertEqual(orig, derived) 1841 self.assertEqual(orig, derived)
1678 1842
1679 def test_more_pickling(self): 1843 def test_more_pickling(self):
1680 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116) 1844 a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
1681 s = pickle.dumps(a) 1845 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
1682 b = pickle.loads(s) 1846 s = pickle.dumps(a, proto)
1683 self.assertEqual(b.year, 2003) 1847 b = pickle.loads(s)
1684 self.assertEqual(b.month, 2) 1848 self.assertEqual(b.year, 2003)
1685 self.assertEqual(b.day, 7) 1849 self.assertEqual(b.month, 2)
1850 self.assertEqual(b.day, 7)
1686 1851
1687 def test_pickling_subclass_datetime(self): 1852 def test_pickling_subclass_datetime(self):
1688 args = 6, 7, 23, 20, 59, 1, 64**2 1853 args = 6, 7, 23, 20, 59, 1, 64**2
1689 orig = SubclassDatetime(*args) 1854 orig = SubclassDatetime(*args)
1690 for pickler, unpickler, proto in pickle_choices: 1855 for pickler, unpickler, proto in pickle_choices:
1691 green = pickler.dumps(orig, proto) 1856 green = pickler.dumps(orig, proto)
1692 derived = unpickler.loads(green) 1857 derived = unpickler.loads(green)
1693 self.assertEqual(orig, derived) 1858 self.assertEqual(orig, derived)
1694 1859
1695 def test_more_compare(self): 1860 def test_more_compare(self):
1696 # The test_compare() inherited from TestDate covers the error cases. 1861 # The test_compare() inherited from TestDate covers the error cases.
1697 # We just want to test lexicographic ordering on the members datetime 1862 # We just want to test lexicographic ordering on the members datetime
1698 # has that date lacks. 1863 # has that date lacks.
1699 args = [2000, 11, 29, 20, 58, 16, 999998] 1864 args = [2000, 11, 29, 20, 58, 16, 999998]
1700 t1 = self.theclass(*args) 1865 t1 = self.theclass(*args)
1701 t2 = self.theclass(*args) 1866 t2 = self.theclass(*args)
1702 self.assertEqual(t1, t2) 1867 self.assertEqual(t1, t2)
1703 self.assertTrue(t1 <= t2) 1868 self.assertTrue(t1 <= t2)
1704 self.assertTrue(t1 >= t2) 1869 self.assertTrue(t1 >= t2)
1705 self.assertTrue(not t1 != t2) 1870 self.assertFalse(t1 != t2)
1706 self.assertTrue(not t1 < t2) 1871 self.assertFalse(t1 < t2)
1707 self.assertTrue(not t1 > t2) 1872 self.assertFalse(t1 > t2)
1708 1873
1709 for i in range(len(args)): 1874 for i in range(len(args)):
1710 newargs = args[:] 1875 newargs = args[:]
1711 newargs[i] = args[i] + 1 1876 newargs[i] = args[i] + 1
1712 t2 = self.theclass(*newargs) # this is larger than t1 1877 t2 = self.theclass(*newargs) # this is larger than t1
1713 self.assertTrue(t1 < t2) 1878 self.assertTrue(t1 < t2)
1714 self.assertTrue(t2 > t1) 1879 self.assertTrue(t2 > t1)
1715 self.assertTrue(t1 <= t2) 1880 self.assertTrue(t1 <= t2)
1716 self.assertTrue(t2 >= t1) 1881 self.assertTrue(t2 >= t1)
1717 self.assertTrue(t1 != t2) 1882 self.assertTrue(t1 != t2)
1718 self.assertTrue(t2 != t1) 1883 self.assertTrue(t2 != t1)
1719 self.assertTrue(not t1 == t2) 1884 self.assertFalse(t1 == t2)
1720 self.assertTrue(not t2 == t1) 1885 self.assertFalse(t2 == t1)
1721 self.assertTrue(not t1 > t2) 1886 self.assertFalse(t1 > t2)
1722 self.assertTrue(not t2 < t1) 1887 self.assertFalse(t2 < t1)
1723 self.assertTrue(not t1 >= t2) 1888 self.assertFalse(t1 >= t2)
1724 self.assertTrue(not t2 <= t1) 1889 self.assertFalse(t2 <= t1)
1725 1890
1726 1891
1727 # A helper for timestamp constructor tests. 1892 # A helper for timestamp constructor tests.
1728 def verify_field_equality(self, expected, got): 1893 def verify_field_equality(self, expected, got):
1729 self.assertEqual(expected.tm_year, got.year) 1894 self.assertEqual(expected.tm_year, got.year)
1730 self.assertEqual(expected.tm_mon, got.month) 1895 self.assertEqual(expected.tm_mon, got.month)
1731 self.assertEqual(expected.tm_mday, got.day) 1896 self.assertEqual(expected.tm_mday, got.day)
1732 self.assertEqual(expected.tm_hour, got.hour) 1897 self.assertEqual(expected.tm_hour, got.hour)
1733 self.assertEqual(expected.tm_min, got.minute) 1898 self.assertEqual(expected.tm_min, got.minute)
1734 self.assertEqual(expected.tm_sec, got.second) 1899 self.assertEqual(expected.tm_sec, got.second)
1735 1900
1736 def test_fromtimestamp(self): 1901 def test_fromtimestamp(self):
1737 import time 1902 import time
1738 1903
1739 ts = time.time() 1904 ts = time.time()
1740 expected = time.localtime(ts) 1905 expected = time.localtime(ts)
1741 got = self.theclass.fromtimestamp(ts) 1906 got = self.theclass.fromtimestamp(ts)
1742 self.verify_field_equality(expected, got) 1907 self.verify_field_equality(expected, got)
1743 1908
1744 def test_utcfromtimestamp(self): 1909 def test_utcfromtimestamp(self):
1745 import time 1910 import time
1746 1911
1747 ts = time.time() 1912 ts = time.time()
1748 expected = time.gmtime(ts) 1913 expected = time.gmtime(ts)
1749 got = self.theclass.utcfromtimestamp(ts) 1914 got = self.theclass.utcfromtimestamp(ts)
1750 self.verify_field_equality(expected, got) 1915 self.verify_field_equality(expected, got)
1916
1917 def test_fromisoformat(self):
1918 self.assertEqual(self.theclass.fromisoformat('2015-12-31T14:27:00'),
1919 self.theclass(2015, 12, 31, 14, 27, 0))
1920 self.assertEqual(self.theclass.fromisoformat('2015-12-31 14:27:00'),
1921 self.theclass(2015, 12, 31, 14, 27, 0))
1922 # lowercase 'T' date-time separator. Uncommon but tolerated (rfc 3339)
1923 self.assertEqual(self.theclass.fromisoformat('2015-12-31t14:27:00'),
1924 self.theclass(2015, 12, 31, 14, 27, 0))
1925
1926 with self.assertRaises(ValueError):
1927 self.theclass.fromisoformat('2015-01-07X00:00:00')
1751 1928
1752 # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in 1929 # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
1753 # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0). 1930 # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
1754 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') 1931 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
1755 def test_timestamp_naive(self): 1932 def test_timestamp_naive(self):
1756 t = self.theclass(1970, 1, 1) 1933 t = self.theclass(1970, 1, 1)
1757 self.assertEqual(t.timestamp(), 18000.0) 1934 self.assertEqual(t.timestamp(), 18000.0)
1758 t = self.theclass(1970, 1, 1, 1, 2, 3, 4) 1935 t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
1759 self.assertEqual(t.timestamp(), 1936 self.assertEqual(t.timestamp(),
1760 18000.0 + 3600 + 2*60 + 3 + 4*1e-6) 1937 18000.0 + 3600 + 2*60 + 3 + 4*1e-6)
1761 # Missing hour may produce platform-dependent result 1938 # Missing hour
1762 t = self.theclass(2012, 3, 11, 2, 30) 1939 t0 = self.theclass(2012, 3, 11, 2, 30)
1763 self.assertIn(self.theclass.fromtimestamp(t.timestamp()), 1940 t1 = t0.replace(fold=1)
1764 [t - timedelta(hours=1), t + timedelta(hours=1)]) 1941 self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()),
1942 t0 - timedelta(hours=1))
1943 self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()),
1944 t1 + timedelta(hours=1))
1765 # Ambiguous hour defaults to DST 1945 # Ambiguous hour defaults to DST
1766 t = self.theclass(2012, 11, 4, 1, 30) 1946 t = self.theclass(2012, 11, 4, 1, 30)
1767 self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t) 1947 self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)
1768 1948
1769 # Timestamp may raise an overflow error on some platforms 1949 # Timestamp may raise an overflow error on some platforms
1770 for t in [self.theclass(1,1,1), self.theclass(9999,12,12)]: 1950 # XXX: Do we care to support the first and last year?
1951 for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]:
1771 try: 1952 try:
1772 s = t.timestamp() 1953 s = t.timestamp()
1773 except OverflowError: 1954 except OverflowError:
1774 pass 1955 pass
1775 else: 1956 else:
1776 self.assertEqual(self.theclass.fromtimestamp(s), t) 1957 self.assertEqual(self.theclass.fromtimestamp(s), t)
1777 1958
1778 def test_timestamp_aware(self): 1959 def test_timestamp_aware(self):
1779 t = self.theclass(1970, 1, 1, tzinfo=timezone.utc) 1960 t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)
1780 self.assertEqual(t.timestamp(), 0.0) 1961 self.assertEqual(t.timestamp(), 0.0)
1781 t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc) 1962 t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)
1782 self.assertEqual(t.timestamp(), 1963 self.assertEqual(t.timestamp(),
1783 3600 + 2*60 + 3 + 4*1e-6) 1964 3600 + 2*60 + 3 + 4*1e-6)
1784 t = self.theclass(1970, 1, 1, 1, 2, 3, 4, 1965 t = self.theclass(1970, 1, 1, 1, 2, 3, 4,
1785 tzinfo=timezone(timedelta(hours=-5), 'EST')) 1966 tzinfo=timezone(timedelta(hours=-5), 'EST'))
1786 self.assertEqual(t.timestamp(), 1967 self.assertEqual(t.timestamp(),
1787 18000 + 3600 + 2*60 + 3 + 4*1e-6) 1968 18000 + 3600 + 2*60 + 3 + 4*1e-6)
1969
1970 @support.run_with_tz('MSK-03') # Something east of Greenwich
1788 def test_microsecond_rounding(self): 1971 def test_microsecond_rounding(self):
1789 for fts in [self.theclass.fromtimestamp, 1972 for fts in [self.theclass.fromtimestamp,
1790 self.theclass.utcfromtimestamp]: 1973 self.theclass.utcfromtimestamp]:
1791 zero = fts(0) 1974 zero = fts(0)
1792 self.assertEqual(zero.second, 0) 1975 self.assertEqual(zero.second, 0)
1793 self.assertEqual(zero.microsecond, 0) 1976 self.assertEqual(zero.microsecond, 0)
1977 one = fts(1e-6)
1794 try: 1978 try:
1795 minus_one = fts(-1e-6) 1979 minus_one = fts(-1e-6)
1796 except OSError: 1980 except OSError:
1797 # localtime(-1) and gmtime(-1) is not supported on Windows 1981 # localtime(-1) and gmtime(-1) is not supported on Windows
1798 pass 1982 pass
1799 else: 1983 else:
1800 self.assertEqual(minus_one.second, 59) 1984 self.assertEqual(minus_one.second, 59)
1801 self.assertEqual(minus_one.microsecond, 999999) 1985 self.assertEqual(minus_one.microsecond, 999999)
1802 1986
1803 t = fts(-1e-8) 1987 t = fts(-1e-8)
1804 self.assertEqual(t, minus_one) 1988 self.assertEqual(t, zero)
1805 t = fts(-9e-7) 1989 t = fts(-9e-7)
1806 self.assertEqual(t, minus_one) 1990 self.assertEqual(t, minus_one)
1807 t = fts(-1e-7) 1991 t = fts(-1e-7)
1808 self.assertEqual(t, minus_one) 1992 self.assertEqual(t, zero)
1993 t = fts(-1/2**7)
1994 self.assertEqual(t.second, 59)
1995 self.assertEqual(t.microsecond, 992188)
1809 1996
1810 t = fts(1e-7) 1997 t = fts(1e-7)
1811 self.assertEqual(t, zero) 1998 self.assertEqual(t, zero)
1812 t = fts(9e-7) 1999 t = fts(9e-7)
1813 self.assertEqual(t, zero) 2000 self.assertEqual(t, one)
1814 t = fts(0.99999949) 2001 t = fts(0.99999949)
1815 self.assertEqual(t.second, 0) 2002 self.assertEqual(t.second, 0)
1816 self.assertEqual(t.microsecond, 999999) 2003 self.assertEqual(t.microsecond, 999999)
1817 t = fts(0.9999999) 2004 t = fts(0.9999999)
2005 self.assertEqual(t.second, 1)
2006 self.assertEqual(t.microsecond, 0)
2007 t = fts(1/2**7)
1818 self.assertEqual(t.second, 0) 2008 self.assertEqual(t.second, 0)
1819 self.assertEqual(t.microsecond, 999999) 2009 self.assertEqual(t.microsecond, 7812)
1820 2010
1821 def test_insane_fromtimestamp(self): 2011 def test_insane_fromtimestamp(self):
1822 # It's possible that some platform maps time_t to double, 2012 # It's possible that some platform maps time_t to double,
1823 # and that this test will fail there. This test should 2013 # and that this test will fail there. This test should
1824 # exempt such platforms (provided they return reasonable 2014 # exempt such platforms (provided they return reasonable
1825 # results!). 2015 # results!).
1826 for insane in -1e200, 1e200: 2016 for insane in -1e200, 1e200:
1827 self.assertRaises(OverflowError, self.theclass.fromtimestamp, 2017 self.assertRaises(OverflowError, self.theclass.fromtimestamp,
1828 insane) 2018 insane)
1829 2019
1830 def test_insane_utcfromtimestamp(self): 2020 def test_insane_utcfromtimestamp(self):
1831 # It's possible that some platform maps time_t to double, 2021 # It's possible that some platform maps time_t to double,
1832 # and that this test will fail there. This test should 2022 # and that this test will fail there. This test should
1833 # exempt such platforms (provided they return reasonable 2023 # exempt such platforms (provided they return reasonable
1834 # results!). 2024 # results!).
1835 for insane in -1e200, 1e200: 2025 for insane in -1e200, 1e200:
1836 self.assertRaises(OverflowError, self.theclass.utcfromtimestamp, 2026 self.assertRaises(OverflowError, self.theclass.utcfromtimestamp,
1837 insane) 2027 insane)
2028
1838 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative t imestamps") 2029 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative t imestamps")
1839 def test_negative_float_fromtimestamp(self): 2030 def test_negative_float_fromtimestamp(self):
1840 # The result is tz-dependent; at least test that this doesn't 2031 # The result is tz-dependent; at least test that this doesn't
1841 # fail (like it did before bug 1646728 was fixed). 2032 # fail (like it did before bug 1646728 was fixed).
1842 self.theclass.fromtimestamp(-1.05) 2033 self.theclass.fromtimestamp(-1.05)
1843 2034
1844 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative t imestamps") 2035 @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative t imestamps")
1845 def test_negative_float_utcfromtimestamp(self): 2036 def test_negative_float_utcfromtimestamp(self):
1846 d = self.theclass.utcfromtimestamp(-1.05) 2037 d = self.theclass.utcfromtimestamp(-1.05)
1847 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000)) 2038 self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
1848 2039
1849 def test_utcnow(self): 2040 def test_utcnow(self):
1850 import time 2041 import time
1851 2042
1852 # Call it a success if utcnow() and utcfromtimestamp() are within 2043 # Call it a success if utcnow() and utcfromtimestamp() are within
1853 # a second of each other. 2044 # a second of each other.
1854 tolerance = timedelta(seconds=1) 2045 tolerance = timedelta(seconds=1)
1855 for dummy in range(3): 2046 for dummy in range(3):
1856 from_now = self.theclass.utcnow() 2047 from_now = self.theclass.utcnow()
1857 from_timestamp = self.theclass.utcfromtimestamp(time.time()) 2048 from_timestamp = self.theclass.utcfromtimestamp(time.time())
1858 if abs(from_timestamp - from_now) <= tolerance: 2049 if abs(from_timestamp - from_now) <= tolerance:
1859 break 2050 break
1860 # Else try again a few times. 2051 # Else try again a few times.
1861 self.assertTrue(abs(from_timestamp - from_now) <= tolerance) 2052 self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
1862 2053
1863 def test_strptime(self): 2054 def test_strptime(self):
1864 string = '2004-12-01 13:02:47.197' 2055 string = '2004-12-01 13:02:47.197'
1865 format = '%Y-%m-%d %H:%M:%S.%f' 2056 format = '%Y-%m-%d %H:%M:%S.%f'
1866 expected = _strptime._strptime_datetime(self.theclass, string, format) 2057 expected = _strptime._strptime_datetime(self.theclass, string, format)
1867 got = self.theclass.strptime(string, format) 2058 got = self.theclass.strptime(string, format)
1868 self.assertEqual(expected, got) 2059 self.assertEqual(expected, got)
1869 self.assertIs(type(expected), self.theclass) 2060 self.assertIs(type(expected), self.theclass)
1870 self.assertIs(type(got), self.theclass) 2061 self.assertIs(type(got), self.theclass)
1871 2062
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
1944 dt = combine(time=t, date=d) 2135 dt = combine(time=t, date=d)
1945 self.assertEqual(dt, expected) 2136 self.assertEqual(dt, expected)
1946 2137
1947 self.assertEqual(d, dt.date()) 2138 self.assertEqual(d, dt.date())
1948 self.assertEqual(t, dt.time()) 2139 self.assertEqual(t, dt.time())
1949 self.assertEqual(dt, combine(dt.date(), dt.time())) 2140 self.assertEqual(dt, combine(dt.date(), dt.time()))
1950 2141
1951 self.assertRaises(TypeError, combine) # need an arg 2142 self.assertRaises(TypeError, combine) # need an arg
1952 self.assertRaises(TypeError, combine, d) # need two args 2143 self.assertRaises(TypeError, combine, d) # need two args
1953 self.assertRaises(TypeError, combine, t, d) # args reversed 2144 self.assertRaises(TypeError, combine, t, d) # args reversed
1954 self.assertRaises(TypeError, combine, d, t, 1) # too many args 2145 self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type
2146 self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args
1955 self.assertRaises(TypeError, combine, "date", "time") # wrong types 2147 self.assertRaises(TypeError, combine, "date", "time") # wrong types
1956 self.assertRaises(TypeError, combine, d, "time") # wrong type 2148 self.assertRaises(TypeError, combine, d, "time") # wrong type
1957 self.assertRaises(TypeError, combine, "date", t) # wrong type 2149 self.assertRaises(TypeError, combine, "date", t) # wrong type
2150
2151 # tzinfo= argument
2152 dt = combine(d, t, timezone.utc)
2153 self.assertIs(dt.tzinfo, timezone.utc)
2154 dt = combine(d, t, tzinfo=timezone.utc)
2155 self.assertIs(dt.tzinfo, timezone.utc)
2156 t = time()
2157 dt = combine(dt, t)
2158 self.assertEqual(dt.date(), d)
2159 self.assertEqual(dt.time(), t)
1958 2160
1959 def test_replace(self): 2161 def test_replace(self):
1960 cls = self.theclass 2162 cls = self.theclass
1961 args = [1, 2, 3, 4, 5, 6, 7] 2163 args = [1, 2, 3, 4, 5, 6, 7]
1962 base = cls(*args) 2164 base = cls(*args)
1963 self.assertEqual(base, base.replace()) 2165 self.assertEqual(base, base.replace())
1964 2166
1965 i = 0 2167 i = 0
1966 for name, newval in (("year", 2), 2168 for name, newval in (("year", 2),
1967 ("month", 3), 2169 ("month", 3),
1968 ("day", 4), 2170 ("day", 4),
1969 ("hour", 5), 2171 ("hour", 5),
1970 ("minute", 6), 2172 ("minute", 6),
1971 ("second", 7), 2173 ("second", 7),
1972 ("microsecond", 8)): 2174 ("microsecond", 8)):
1973 newargs = args[:] 2175 newargs = args[:]
1974 newargs[i] = newval 2176 newargs[i] = newval
1975 expected = cls(*newargs) 2177 expected = cls(*newargs)
1976 got = base.replace(**{name: newval}) 2178 got = base.replace(**{name: newval})
1977 self.assertEqual(expected, got) 2179 self.assertEqual(expected, got)
1978 i += 1 2180 i += 1
1979 2181
1980 # Out of bounds. 2182 # Out of bounds.
1981 base = cls(2000, 2, 29) 2183 base = cls(2000, 2, 29)
1982 self.assertRaises(ValueError, base.replace, year=2001) 2184 self.assertRaises(ValueError, base.replace, year=2001)
1983 2185
1984 def test_astimezone(self): 2186 def test_astimezone(self):
2187 return # The rest is no longer applicable
1985 # Pretty boring! The TZ test is more interesting here. astimezone() 2188 # Pretty boring! The TZ test is more interesting here. astimezone()
1986 # simply can't be applied to a naive object. 2189 # simply can't be applied to a naive object.
1987 dt = self.theclass.now() 2190 dt = self.theclass.now()
1988 f = FixedOffset(44, "") 2191 f = FixedOffset(44, "")
1989 self.assertRaises(ValueError, dt.astimezone) # naive 2192 self.assertRaises(ValueError, dt.astimezone) # naive
1990 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args 2193 self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
1991 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type 2194 self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
1992 self.assertRaises(ValueError, dt.astimezone, f) # naive 2195 self.assertRaises(ValueError, dt.astimezone, f) # naive
1993 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive 2196 self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
1994 2197
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
2029 self.assertEqual(dt2.__class__, C) 2232 self.assertEqual(dt2.__class__, C)
2030 self.assertEqual(dt2.theAnswer, 42) 2233 self.assertEqual(dt2.theAnswer, 42)
2031 self.assertEqual(dt2.extra, 7) 2234 self.assertEqual(dt2.extra, 7)
2032 self.assertEqual(dt1.toordinal(), dt2.toordinal()) 2235 self.assertEqual(dt1.toordinal(), dt2.toordinal())
2033 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month + 2236 self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
2034 dt1.second - 7) 2237 dt1.second - 7)
2035 2238
2036 class TestSubclassDateTime(TestDateTime): 2239 class TestSubclassDateTime(TestDateTime):
2037 theclass = SubclassDatetime 2240 theclass = SubclassDatetime
2038 # Override tests not designed for subclass 2241 # Override tests not designed for subclass
2242 @unittest.skip('not appropriate for subclasses')
2039 def test_roundtrip(self): 2243 def test_roundtrip(self):
2040 pass 2244 pass
2041 2245
2042 class SubclassTime(time): 2246 class SubclassTime(time):
2043 sub_var = 1 2247 sub_var = 1
2044 2248
2045 class TestTime(HarmlessMixedComparison, unittest.TestCase): 2249 class TestTime(HarmlessMixedComparison, unittest.TestCase):
2046 2250
2047 theclass = time 2251 theclass = time
2048 2252
(...skipping 28 matching lines...) Expand all
2077 t.microsecond) 2281 t.microsecond)
2078 self.assertEqual(t, t2) 2282 self.assertEqual(t, t2)
2079 2283
2080 def test_comparing(self): 2284 def test_comparing(self):
2081 args = [1, 2, 3, 4] 2285 args = [1, 2, 3, 4]
2082 t1 = self.theclass(*args) 2286 t1 = self.theclass(*args)
2083 t2 = self.theclass(*args) 2287 t2 = self.theclass(*args)
2084 self.assertEqual(t1, t2) 2288 self.assertEqual(t1, t2)
2085 self.assertTrue(t1 <= t2) 2289 self.assertTrue(t1 <= t2)
2086 self.assertTrue(t1 >= t2) 2290 self.assertTrue(t1 >= t2)
2087 self.assertTrue(not t1 != t2) 2291 self.assertFalse(t1 != t2)
2088 self.assertTrue(not t1 < t2) 2292 self.assertFalse(t1 < t2)
2089 self.assertTrue(not t1 > t2) 2293 self.assertFalse(t1 > t2)
2090 2294
2091 for i in range(len(args)): 2295 for i in range(len(args)):
2092 newargs = args[:] 2296 newargs = args[:]
2093 newargs[i] = args[i] + 1 2297 newargs[i] = args[i] + 1
2094 t2 = self.theclass(*newargs) # this is larger than t1 2298 t2 = self.theclass(*newargs) # this is larger than t1
2095 self.assertTrue(t1 < t2) 2299 self.assertTrue(t1 < t2)
2096 self.assertTrue(t2 > t1) 2300 self.assertTrue(t2 > t1)
2097 self.assertTrue(t1 <= t2) 2301 self.assertTrue(t1 <= t2)
2098 self.assertTrue(t2 >= t1) 2302 self.assertTrue(t2 >= t1)
2099 self.assertTrue(t1 != t2) 2303 self.assertTrue(t1 != t2)
2100 self.assertTrue(t2 != t1) 2304 self.assertTrue(t2 != t1)
2101 self.assertTrue(not t1 == t2) 2305 self.assertFalse(t1 == t2)
2102 self.assertTrue(not t2 == t1) 2306 self.assertFalse(t2 == t1)
2103 self.assertTrue(not t1 > t2) 2307 self.assertFalse(t1 > t2)
2104 self.assertTrue(not t2 < t1) 2308 self.assertFalse(t2 < t1)
2105 self.assertTrue(not t1 >= t2) 2309 self.assertFalse(t1 >= t2)
2106 self.assertTrue(not t2 <= t1) 2310 self.assertFalse(t2 <= t1)
2107 2311
2108 for badarg in OTHERSTUFF: 2312 for badarg in OTHERSTUFF:
2109 self.assertEqual(t1 == badarg, False) 2313 self.assertEqual(t1 == badarg, False)
2110 self.assertEqual(t1 != badarg, True) 2314 self.assertEqual(t1 != badarg, True)
2111 self.assertEqual(badarg == t1, False) 2315 self.assertEqual(badarg == t1, False)
2112 self.assertEqual(badarg != t1, True) 2316 self.assertEqual(badarg != t1, True)
2113 2317
2114 self.assertRaises(TypeError, lambda: t1 <= badarg) 2318 self.assertRaises(TypeError, lambda: t1 <= badarg)
2115 self.assertRaises(TypeError, lambda: t1 < badarg) 2319 self.assertRaises(TypeError, lambda: t1 < badarg)
2116 self.assertRaises(TypeError, lambda: t1 > badarg) 2320 self.assertRaises(TypeError, lambda: t1 > badarg)
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
2162 dic = {d: 1} 2366 dic = {d: 1}
2163 dic[e] = 2 2367 dic[e] = 2
2164 self.assertEqual(len(dic), 1) 2368 self.assertEqual(len(dic), 1)
2165 self.assertEqual(dic[d], 2) 2369 self.assertEqual(dic[d], 2)
2166 self.assertEqual(dic[e], 2) 2370 self.assertEqual(dic[e], 2)
2167 2371
2168 def test_isoformat(self): 2372 def test_isoformat(self):
2169 t = self.theclass(4, 5, 1, 123) 2373 t = self.theclass(4, 5, 1, 123)
2170 self.assertEqual(t.isoformat(), "04:05:01.000123") 2374 self.assertEqual(t.isoformat(), "04:05:01.000123")
2171 self.assertEqual(t.isoformat(), str(t)) 2375 self.assertEqual(t.isoformat(), str(t))
2172 self.assertEqual(self.theclass(t.isoformat()), t)
2173 2376
2174 t = self.theclass() 2377 t = self.theclass()
2175 self.assertEqual(t.isoformat(), "00:00:00") 2378 self.assertEqual(t.isoformat(), "00:00:00")
2176 self.assertEqual(t.isoformat(), str(t)) 2379 self.assertEqual(t.isoformat(), str(t))
2177 self.assertEqual(self.theclass(t.isoformat()), t)
2178 2380
2179 t = self.theclass(microsecond=1) 2381 t = self.theclass(microsecond=1)
2180 self.assertEqual(t.isoformat(), "00:00:00.000001") 2382 self.assertEqual(t.isoformat(), "00:00:00.000001")
2181 self.assertEqual(t.isoformat(), str(t)) 2383 self.assertEqual(t.isoformat(), str(t))
2182 self.assertEqual(self.theclass(t.isoformat()), t)
2183 2384
2184 t = self.theclass(microsecond=10) 2385 t = self.theclass(microsecond=10)
2185 self.assertEqual(t.isoformat(), "00:00:00.000010") 2386 self.assertEqual(t.isoformat(), "00:00:00.000010")
2186 self.assertEqual(t.isoformat(), str(t)) 2387 self.assertEqual(t.isoformat(), str(t))
2187 self.assertEqual(self.theclass(t.isoformat()), t)
2188 2388
2189 t = self.theclass(microsecond=100) 2389 t = self.theclass(microsecond=100)
2190 self.assertEqual(t.isoformat(), "00:00:00.000100") 2390 self.assertEqual(t.isoformat(), "00:00:00.000100")
2191 self.assertEqual(t.isoformat(), str(t)) 2391 self.assertEqual(t.isoformat(), str(t))
2192 self.assertEqual(self.theclass(t.isoformat()), t)
2193 2392
2194 t = self.theclass(microsecond=1000) 2393 t = self.theclass(microsecond=1000)
2195 self.assertEqual(t.isoformat(), "00:00:00.001000") 2394 self.assertEqual(t.isoformat(), "00:00:00.001000")
2196 self.assertEqual(t.isoformat(), str(t)) 2395 self.assertEqual(t.isoformat(), str(t))
2197 self.assertEqual(self.theclass(t.isoformat()), t)
2198 2396
2199 t = self.theclass(microsecond=10000) 2397 t = self.theclass(microsecond=10000)
2200 self.assertEqual(t.isoformat(), "00:00:00.010000") 2398 self.assertEqual(t.isoformat(), "00:00:00.010000")
2201 self.assertEqual(t.isoformat(), str(t)) 2399 self.assertEqual(t.isoformat(), str(t))
2202 self.assertEqual(self.theclass(t.isoformat()), t)
2203 2400
2204 t = self.theclass(microsecond=100000) 2401 t = self.theclass(microsecond=100000)
2205 self.assertEqual(t.isoformat(), "00:00:00.100000") 2402 self.assertEqual(t.isoformat(), "00:00:00.100000")
2206 self.assertEqual(t.isoformat(), str(t)) 2403 self.assertEqual(t.isoformat(), str(t))
2207 self.assertEqual(self.theclass(t.isoformat()), t) 2404
2405 t = self.theclass(hour=12, minute=34, second=56, microsecond=123456)
2406 self.assertEqual(t.isoformat(timespec='hours'), "12")
2407 self.assertEqual(t.isoformat(timespec='minutes'), "12:34")
2408 self.assertEqual(t.isoformat(timespec='seconds'), "12:34:56")
2409 self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.123")
2410 self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.123456" )
2411 self.assertEqual(t.isoformat(timespec='auto'), "12:34:56.123456")
2412 self.assertRaises(ValueError, t.isoformat, timespec='monkey')
2413
2414 t = self.theclass(hour=12, minute=34, second=56, microsecond=999500)
2415 self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.999")
2416
2417 t = self.theclass(hour=12, minute=34, second=56, microsecond=0)
2418 self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.000")
2419 self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.000000" )
2420 self.assertEqual(t.isoformat(timespec='auto'), "12:34:56")
2421
2422 def test_fromisoformat(self):
2423 # basic
2424 self.assertEqual(self.theclass.fromisoformat('04:05:01.000123'),
2425 self.theclass(4, 5, 1, 123))
2426 self.assertEqual(self.theclass.fromisoformat('00:00:00'),
2427 self.theclass(0, 0, 0))
2428 # usec, rounding high
2429 self.assertEqual(self.theclass.fromisoformat('10:20:30.40000059'),
2430 self.theclass(10, 20, 30, 400001))
2431 # usec, rounding low + long digits we don't care about
2432 self.assertEqual(self.theclass.fromisoformat('10:20:30.400003434'),
2433 self.theclass(10, 20, 30, 400003))
2434 with self.assertRaises(ValueError):
2435 self.theclass.fromisoformat('12:00AM')
2436 with self.assertRaises(ValueError):
2437 self.theclass.fromisoformat('120000')
2438 with self.assertRaises(ValueError):
2439 self.theclass.fromisoformat('1:00')
2440 with self.assertRaises(ValueError):
2441 self.theclass.fromisoformat('17:54:43.')
2442
2443 def tz(h, m):
2444 return timezone(timedelta(hours=h, minutes=m))
2445
2446 self.assertEqual(self.theclass.fromisoformat('00:00:00Z'),
2447 self.theclass(0, 0, 0, tzinfo=timezone.utc))
2448 # lowercase UTC timezone. Uncommon but tolerated (rfc 3339)
2449 self.assertEqual(self.theclass.fromisoformat('00:00:00z'),
2450 self.theclass(0, 0, 0, tzinfo=timezone.utc))
2451 self.assertEqual(self.theclass.fromisoformat('00:00:00-00:00'),
2452 self.theclass(0, 0, 0, tzinfo=tz(0, 0)))
2453 self.assertEqual(self.theclass.fromisoformat('08:30:00.004255+02:30'),
2454 self.theclass(8, 30, 0, 4255, tz(2, 30)))
2455 self.assertEqual(self.theclass.fromisoformat('08:30:00.004255-02:30'),
2456 self.theclass(8, 30, 0, 4255, tz(-2, -30)))
2208 2457
2209 def test_1653736(self): 2458 def test_1653736(self):
2210 # verify it doesn't accept extra keyword arguments 2459 # verify it doesn't accept extra keyword arguments
2211 t = self.theclass(second=1) 2460 t = self.theclass(second=1)
2212 self.assertRaises(TypeError, t.isoformat, foo=3) 2461 self.assertRaises(TypeError, t.isoformat, foo=3)
2213 2462
2214 def test_strftime(self): 2463 def test_strftime(self):
2215 t = self.theclass(1, 2, 3, 4) 2464 t = self.theclass(1, 2, 3, 4)
2216 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004") 2465 self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
2217 # A naive object replaces %z and %Z with empty strings. 2466 # A naive object replaces %z and %Z with empty strings.
2218 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''") 2467 self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
2219 2468
2220 def test_format(self): 2469 def test_format(self):
2221 t = self.theclass(1, 2, 3, 4) 2470 t = self.theclass(1, 2, 3, 4)
2222 self.assertEqual(t.__format__(''), str(t)) 2471 self.assertEqual(t.__format__(''), str(t))
2472
2473 with self.assertRaisesRegex(TypeError, 'must be str, not int'):
2474 t.__format__(123)
2223 2475
2224 # check that a derived class's __str__() gets called 2476 # check that a derived class's __str__() gets called
2225 class A(self.theclass): 2477 class A(self.theclass):
2226 def __str__(self): 2478 def __str__(self):
2227 return 'A' 2479 return 'A'
2228 a = A(1, 2, 3, 4) 2480 a = A(1, 2, 3, 4)
2229 self.assertEqual(a.__format__(''), 'A') 2481 self.assertEqual(a.__format__(''), 'A')
2230 2482
2231 # check that a derived class's strftime gets called 2483 # check that a derived class's strftime gets called
2232 class B(self.theclass): 2484 class B(self.theclass):
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
2277 2529
2278 def test_pickling_subclass_time(self): 2530 def test_pickling_subclass_time(self):
2279 args = 20, 59, 16, 64**2 2531 args = 20, 59, 16, 64**2
2280 orig = SubclassTime(*args) 2532 orig = SubclassTime(*args)
2281 for pickler, unpickler, proto in pickle_choices: 2533 for pickler, unpickler, proto in pickle_choices:
2282 green = pickler.dumps(orig, proto) 2534 green = pickler.dumps(orig, proto)
2283 derived = unpickler.loads(green) 2535 derived = unpickler.loads(green)
2284 self.assertEqual(orig, derived) 2536 self.assertEqual(orig, derived)
2285 2537
2286 def test_bool(self): 2538 def test_bool(self):
2539 # time is always True.
2287 cls = self.theclass 2540 cls = self.theclass
2288 self.assertTrue(cls(1)) 2541 self.assertTrue(cls(1))
2289 self.assertTrue(cls(0, 1)) 2542 self.assertTrue(cls(0, 1))
2290 self.assertTrue(cls(0, 0, 1)) 2543 self.assertTrue(cls(0, 0, 1))
2291 self.assertTrue(cls(0, 0, 0, 1)) 2544 self.assertTrue(cls(0, 0, 0, 1))
2292 self.assertTrue(not cls(0)) 2545 self.assertTrue(cls(0))
2293 self.assertTrue(not cls()) 2546 self.assertTrue(cls())
2294 2547
2295 def test_replace(self): 2548 def test_replace(self):
2296 cls = self.theclass 2549 cls = self.theclass
2297 args = [1, 2, 3, 4] 2550 args = [1, 2, 3, 4]
2298 base = cls(*args) 2551 base = cls(*args)
2299 self.assertEqual(base, base.replace()) 2552 self.assertEqual(base, base.replace())
2300 2553
2301 i = 0 2554 i = 0
2302 for name, newval in (("hour", 5), 2555 for name, newval in (("hour", 5),
2303 ("minute", 6), 2556 ("minute", 6),
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
2338 dt2 = C(*args, **{'extra': 7}) 2591 dt2 = C(*args, **{'extra': 7})
2339 2592
2340 self.assertEqual(dt2.__class__, C) 2593 self.assertEqual(dt2.__class__, C)
2341 self.assertEqual(dt2.theAnswer, 42) 2594 self.assertEqual(dt2.theAnswer, 42)
2342 self.assertEqual(dt2.extra, 7) 2595 self.assertEqual(dt2.extra, 7)
2343 self.assertEqual(dt1.isoformat(), dt2.isoformat()) 2596 self.assertEqual(dt1.isoformat(), dt2.isoformat())
2344 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7) 2597 self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
2345 2598
2346 def test_backdoor_resistance(self): 2599 def test_backdoor_resistance(self):
2347 # see TestDate.test_backdoor_resistance(). 2600 # see TestDate.test_backdoor_resistance().
2348 base = b'2:59.0' 2601 base = '2:59.0'
2349 for hour_byte in b' ', b'9', bytes([24]), b'\xff': 2602 for hour_byte in ' ', '9', chr(24), '\xff':
2350 self.assertRaises(TypeError, self.theclass, 2603 self.assertRaises(TypeError, self.theclass,
2351 hour_byte + base[1:]) 2604 hour_byte + base[1:])
2352 2605 # Good bytes, but bad tzinfo:
2353 def test_from_string(self): 2606 with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
2354 s = "00" 2607 self.theclass(bytes([1] * len(base)), 'EST')
2355 self.assertEqual(self.theclass(s), self.theclass(0))
2356 s = "00:00"
2357 self.assertEqual(self.theclass(s), self.theclass(0))
2358 s = "00:00:00"
2359 self.assertEqual(self.theclass(s), self.theclass(0))
2360 s = "00:00:00.000000"
2361 self.assertEqual(self.theclass(s), self.theclass(0))
2362 tz = timezone.utc
2363 s = "00Z"
2364 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2365 s = "00:00Z"
2366 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2367 s = "00:00:00Z"
2368 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2369 s = "00:00:00.000000Z"
2370 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2371 s = "00+0000"
2372 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2373 s = "00:00+0000"
2374 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2375 s = "00:00:00+0000"
2376 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2377 s = "00:00:00.000000+0000"
2378 self.assertEqual(self.theclass(s), self.theclass(0, tzinfo=tz))
2379
2380 2608
2381 # A mixin for classes with a tzinfo= argument. Subclasses must define 2609 # A mixin for classes with a tzinfo= argument. Subclasses must define
2382 # theclass as a class atribute, and theclass(1, 1, 1, tzinfo=whatever) 2610 # theclass as a class attribute, and theclass(1, 1, 1, tzinfo=whatever)
2383 # must be legit (which is true for time and datetime). 2611 # must be legit (which is true for time and datetime).
2384 class TZInfoBase: 2612 class TZInfoBase:
2385 2613
2386 def test_argument_passing(self): 2614 def test_argument_passing(self):
2387 cls = self.theclass 2615 cls = self.theclass
2388 # A datetime passes itself on, a time passes None. 2616 # A datetime passes itself on, a time passes None.
2389 class introspective(tzinfo): 2617 class introspective(tzinfo):
2390 def tzname(self, dt): return dt and "real" or "none" 2618 def tzname(self, dt): return dt and "real" or "none"
2391 def utcoffset(self, dt): 2619 def utcoffset(self, dt):
2392 return timedelta(minutes = dt and 42 or -42) 2620 return timedelta(minutes = dt and 42 or -42)
(...skipping 15 matching lines...) Expand all
2408 class NiceTry(object): 2636 class NiceTry(object):
2409 def __init__(self): pass 2637 def __init__(self): pass
2410 def utcoffset(self, dt): pass 2638 def utcoffset(self, dt): pass
2411 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry) 2639 self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
2412 2640
2413 class BetterTry(tzinfo): 2641 class BetterTry(tzinfo):
2414 def __init__(self): pass 2642 def __init__(self): pass
2415 def utcoffset(self, dt): pass 2643 def utcoffset(self, dt): pass
2416 b = BetterTry() 2644 b = BetterTry()
2417 t = cls(1, 1, 1, tzinfo=b) 2645 t = cls(1, 1, 1, tzinfo=b)
2418 self.assertTrue(t.tzinfo is b) 2646 self.assertIs(t.tzinfo, b)
2419 2647
2420 def test_utc_offset_out_of_bounds(self): 2648 def test_utc_offset_out_of_bounds(self):
2421 class Edgy(tzinfo): 2649 class Edgy(tzinfo):
2422 def __init__(self, offset): 2650 def __init__(self, offset):
2423 self.offset = timedelta(minutes=offset) 2651 self.offset = timedelta(minutes=offset)
2424 def utcoffset(self, dt): 2652 def utcoffset(self, dt):
2425 return self.offset 2653 return self.offset
2426 2654
2427 cls = self.theclass 2655 cls = self.theclass
2428 for offset, legit in ((-1440, False), 2656 for offset, legit in ((-1440, False),
(...skipping 18 matching lines...) Expand all
2447 2675
2448 def test_tzinfo_classes(self): 2676 def test_tzinfo_classes(self):
2449 cls = self.theclass 2677 cls = self.theclass
2450 class C1(tzinfo): 2678 class C1(tzinfo):
2451 def utcoffset(self, dt): return None 2679 def utcoffset(self, dt): return None
2452 def dst(self, dt): return None 2680 def dst(self, dt): return None
2453 def tzname(self, dt): return None 2681 def tzname(self, dt): return None
2454 for t in (cls(1, 1, 1), 2682 for t in (cls(1, 1, 1),
2455 cls(1, 1, 1, tzinfo=None), 2683 cls(1, 1, 1, tzinfo=None),
2456 cls(1, 1, 1, tzinfo=C1())): 2684 cls(1, 1, 1, tzinfo=C1())):
2457 self.assertTrue(t.utcoffset() is None) 2685 self.assertIsNone(t.utcoffset())
2458 self.assertTrue(t.dst() is None) 2686 self.assertIsNone(t.dst())
2459 self.assertTrue(t.tzname() is None) 2687 self.assertIsNone(t.tzname())
2460 2688
2461 class C3(tzinfo): 2689 class C3(tzinfo):
2462 def utcoffset(self, dt): return timedelta(minutes=-1439) 2690 def utcoffset(self, dt): return timedelta(minutes=-1439)
2463 def dst(self, dt): return timedelta(minutes=1439) 2691 def dst(self, dt): return timedelta(minutes=1439)
2464 def tzname(self, dt): return "aname" 2692 def tzname(self, dt): return "aname"
2465 t = cls(1, 1, 1, tzinfo=C3()) 2693 t = cls(1, 1, 1, tzinfo=C3())
2466 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439)) 2694 self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
2467 self.assertEqual(t.dst(), timedelta(minutes=1439)) 2695 self.assertEqual(t.dst(), timedelta(minutes=1439))
2468 self.assertEqual(t.tzname(), "aname") 2696 self.assertEqual(t.tzname(), "aname")
2469 2697
2470 # Wrong types. 2698 # Wrong types.
2471 class C4(tzinfo): 2699 class C4(tzinfo):
2472 def utcoffset(self, dt): return "aname" 2700 def utcoffset(self, dt): return "aname"
2473 def dst(self, dt): return 7 2701 def dst(self, dt): return 7
2474 def tzname(self, dt): return 0 2702 def tzname(self, dt): return 0
2475 t = cls(1, 1, 1, tzinfo=C4()) 2703 t = cls(1, 1, 1, tzinfo=C4())
2476 self.assertRaises(TypeError, t.utcoffset) 2704 self.assertRaises(TypeError, t.utcoffset)
2477 self.assertRaises(TypeError, t.dst) 2705 self.assertRaises(TypeError, t.dst)
2478 self.assertRaises(TypeError, t.tzname) 2706 self.assertRaises(TypeError, t.tzname)
2479 2707
2480 # Offset out of range. 2708 # Offset out of range.
2481 class C6(tzinfo): 2709 class C6(tzinfo):
2482 def utcoffset(self, dt): return timedelta(hours=-24) 2710 def utcoffset(self, dt): return timedelta(hours=-24)
2483 def dst(self, dt): return timedelta(hours=24) 2711 def dst(self, dt): return timedelta(hours=24)
2484 t = cls(1, 1, 1, tzinfo=C6()) 2712 t = cls(1, 1, 1, tzinfo=C6())
2485 self.assertRaises(ValueError, t.utcoffset) 2713 self.assertRaises(ValueError, t.utcoffset)
2486 self.assertRaises(ValueError, t.dst) 2714 self.assertRaises(ValueError, t.dst)
2487 2715
2488 # Not a whole number of minutes. 2716 # Not a whole number of seconds.
2489 class C7(tzinfo): 2717 class C7(tzinfo):
2490 def utcoffset(self, dt): return timedelta(seconds=61) 2718 def utcoffset(self, dt): return timedelta(microseconds=61)
2491 def dst(self, dt): return timedelta(microseconds=-81) 2719 def dst(self, dt): return timedelta(microseconds=-81)
2492 t = cls(1, 1, 1, tzinfo=C7()) 2720 t = cls(1, 1, 1, tzinfo=C7())
2493 self.assertRaises(ValueError, t.utcoffset) 2721 self.assertRaises(ValueError, t.utcoffset)
2494 self.assertRaises(ValueError, t.dst) 2722 self.assertRaises(ValueError, t.dst)
2495 2723
2496 def test_aware_compare(self): 2724 def test_aware_compare(self):
2497 cls = self.theclass 2725 cls = self.theclass
2498 2726
2499 # Ensure that utcoffset() gets ignored if the comparands have 2727 # Ensure that utcoffset() gets ignored if the comparands have
2500 # the same tzinfo member. 2728 # the same tzinfo member.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
2544 # Testing time objects with a non-None tzinfo. 2772 # Testing time objects with a non-None tzinfo.
2545 class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase): 2773 class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
2546 theclass = time 2774 theclass = time
2547 2775
2548 def test_empty(self): 2776 def test_empty(self):
2549 t = self.theclass() 2777 t = self.theclass()
2550 self.assertEqual(t.hour, 0) 2778 self.assertEqual(t.hour, 0)
2551 self.assertEqual(t.minute, 0) 2779 self.assertEqual(t.minute, 0)
2552 self.assertEqual(t.second, 0) 2780 self.assertEqual(t.second, 0)
2553 self.assertEqual(t.microsecond, 0) 2781 self.assertEqual(t.microsecond, 0)
2554 self.assertTrue(t.tzinfo is None) 2782 self.assertIsNone(t.tzinfo)
2555 2783
2556 def test_zones(self): 2784 def test_zones(self):
2557 est = FixedOffset(-300, "EST", 1) 2785 est = FixedOffset(-300, "EST", 1)
2558 utc = FixedOffset(0, "UTC", -2) 2786 utc = FixedOffset(0, "UTC", -2)
2559 met = FixedOffset(60, "MET", 3) 2787 met = FixedOffset(60, "MET", 3)
2560 t1 = time( 7, 47, tzinfo=est) 2788 t1 = time( 7, 47, tzinfo=est)
2561 t2 = time(12, 47, tzinfo=utc) 2789 t2 = time(12, 47, tzinfo=utc)
2562 t3 = time(13, 47, tzinfo=met) 2790 t3 = time(13, 47, tzinfo=met)
2563 t4 = time(microsecond=40) 2791 t4 = time(microsecond=40)
2564 t5 = time(microsecond=40, tzinfo=utc) 2792 t5 = time(microsecond=40, tzinfo=utc)
2565 2793
2566 self.assertEqual(t1.tzinfo, est) 2794 self.assertEqual(t1.tzinfo, est)
2567 self.assertEqual(t2.tzinfo, utc) 2795 self.assertEqual(t2.tzinfo, utc)
2568 self.assertEqual(t3.tzinfo, met) 2796 self.assertEqual(t3.tzinfo, met)
2569 self.assertTrue(t4.tzinfo is None) 2797 self.assertIsNone(t4.tzinfo)
2570 self.assertEqual(t5.tzinfo, utc) 2798 self.assertEqual(t5.tzinfo, utc)
2571 2799
2572 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300)) 2800 self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
2573 self.assertEqual(t2.utcoffset(), timedelta(minutes=0)) 2801 self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
2574 self.assertEqual(t3.utcoffset(), timedelta(minutes=60)) 2802 self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
2575 self.assertTrue(t4.utcoffset() is None) 2803 self.assertIsNone(t4.utcoffset())
2576 self.assertRaises(TypeError, t1.utcoffset, "no args") 2804 self.assertRaises(TypeError, t1.utcoffset, "no args")
2577 2805
2578 self.assertEqual(t1.tzname(), "EST") 2806 self.assertEqual(t1.tzname(), "EST")
2579 self.assertEqual(t2.tzname(), "UTC") 2807 self.assertEqual(t2.tzname(), "UTC")
2580 self.assertEqual(t3.tzname(), "MET") 2808 self.assertEqual(t3.tzname(), "MET")
2581 self.assertTrue(t4.tzname() is None) 2809 self.assertIsNone(t4.tzname())
2582 self.assertRaises(TypeError, t1.tzname, "no args") 2810 self.assertRaises(TypeError, t1.tzname, "no args")
2583 2811
2584 self.assertEqual(t1.dst(), timedelta(minutes=1)) 2812 self.assertEqual(t1.dst(), timedelta(minutes=1))
2585 self.assertEqual(t2.dst(), timedelta(minutes=-2)) 2813 self.assertEqual(t2.dst(), timedelta(minutes=-2))
2586 self.assertEqual(t3.dst(), timedelta(minutes=3)) 2814 self.assertEqual(t3.dst(), timedelta(minutes=3))
2587 self.assertTrue(t4.dst() is None) 2815 self.assertIsNone(t4.dst())
2588 self.assertRaises(TypeError, t1.dst, "no args") 2816 self.assertRaises(TypeError, t1.dst, "no args")
2589 2817
2590 self.assertEqual(hash(t1), hash(t2)) 2818 self.assertEqual(hash(t1), hash(t2))
2591 self.assertEqual(hash(t1), hash(t3)) 2819 self.assertEqual(hash(t1), hash(t3))
2592 self.assertEqual(hash(t2), hash(t3)) 2820 self.assertEqual(hash(t2), hash(t3))
2593 2821
2594 self.assertEqual(t1, t2) 2822 self.assertEqual(t1, t2)
2595 self.assertEqual(t1, t3) 2823 self.assertEqual(t1, t3)
2596 self.assertEqual(t2, t3) 2824 self.assertEqual(t2, t3)
2597 self.assertNotEqual(t4, t5) # mixed tz-aware & naive 2825 self.assertNotEqual(t4, t5) # mixed tz-aware & naive
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
2629 2857
2630 # Check that an invalid tzname result raises an exception. 2858 # Check that an invalid tzname result raises an exception.
2631 class Badtzname(tzinfo): 2859 class Badtzname(tzinfo):
2632 tz = 42 2860 tz = 42
2633 def tzname(self, dt): return self.tz 2861 def tzname(self, dt): return self.tz
2634 t = time(2, 3, 4, tzinfo=Badtzname()) 2862 t = time(2, 3, 4, tzinfo=Badtzname())
2635 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04") 2863 self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
2636 self.assertRaises(TypeError, t.strftime, "%Z") 2864 self.assertRaises(TypeError, t.strftime, "%Z")
2637 2865
2638 # Issue #6697: 2866 # Issue #6697:
2639 if '_Fast' in str(type(self)): 2867 if '_Fast' in str(self):
2640 Badtzname.tz = '\ud800' 2868 Badtzname.tz = '\ud800'
2641 self.assertRaises(ValueError, t.strftime, "%Z") 2869 self.assertRaises(ValueError, t.strftime, "%Z")
2642 2870
2643 def test_hash_edge_cases(self): 2871 def test_hash_edge_cases(self):
2644 # Offsets that overflow a basic time. 2872 # Offsets that overflow a basic time.
2645 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, "")) 2873 t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
2646 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, "")) 2874 t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
2647 self.assertEqual(hash(t1), hash(t2)) 2875 self.assertEqual(hash(t1), hash(t2))
2648 2876
2649 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, "")) 2877 t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
(...skipping 14 matching lines...) Expand all
2664 orig = self.theclass(5, 6, 7, tzinfo=tinfo) 2892 orig = self.theclass(5, 6, 7, tzinfo=tinfo)
2665 for pickler, unpickler, proto in pickle_choices: 2893 for pickler, unpickler, proto in pickle_choices:
2666 green = pickler.dumps(orig, proto) 2894 green = pickler.dumps(orig, proto)
2667 derived = unpickler.loads(green) 2895 derived = unpickler.loads(green)
2668 self.assertEqual(orig, derived) 2896 self.assertEqual(orig, derived)
2669 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset) 2897 self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
2670 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300)) 2898 self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
2671 self.assertEqual(derived.tzname(), 'cookie') 2899 self.assertEqual(derived.tzname(), 'cookie')
2672 2900
2673 def test_more_bool(self): 2901 def test_more_bool(self):
2674 # Test cases with non-None tzinfo. 2902 # time is always True.
2675 cls = self.theclass 2903 cls = self.theclass
2676 2904
2677 t = cls(0, tzinfo=FixedOffset(-300, "")) 2905 t = cls(0, tzinfo=FixedOffset(-300, ""))
2678 self.assertTrue(t) 2906 self.assertTrue(t)
2679 2907
2680 t = cls(5, tzinfo=FixedOffset(-300, "")) 2908 t = cls(5, tzinfo=FixedOffset(-300, ""))
2681 self.assertTrue(t) 2909 self.assertTrue(t)
2682 2910
2683 t = cls(5, tzinfo=FixedOffset(300, "")) 2911 t = cls(5, tzinfo=FixedOffset(300, ""))
2684 self.assertTrue(not t) 2912 self.assertTrue(t)
2685 2913
2686 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, "")) 2914 t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
2687 self.assertTrue(not t)
2688
2689 # Mostly ensuring this doesn't overflow internally.
2690 t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
2691 self.assertTrue(t) 2915 self.assertTrue(t)
2692
2693 # But this should yield a value error -- the utcoffset is bogus.
2694 t = cls(0, tzinfo=FixedOffset(24*60, ""))
2695 self.assertRaises(ValueError, lambda: bool(t))
2696
2697 # Likewise.
2698 t = cls(0, tzinfo=FixedOffset(-24*60, ""))
2699 self.assertRaises(ValueError, lambda: bool(t))
2700 2916
2701 def test_replace(self): 2917 def test_replace(self):
2702 cls = self.theclass 2918 cls = self.theclass
2703 z100 = FixedOffset(100, "+100") 2919 z100 = FixedOffset(100, "+100")
2704 zm200 = FixedOffset(timedelta(minutes=-200), "-200") 2920 zm200 = FixedOffset(timedelta(minutes=-200), "-200")
2705 args = [1, 2, 3, 4, z100] 2921 args = [1, 2, 3, 4, z100]
2706 base = cls(*args) 2922 base = cls(*args)
2707 self.assertEqual(base, base.replace()) 2923 self.assertEqual(base, base.replace())
2708 2924
2709 i = 0 2925 i = 0
2710 for name, newval in (("hour", 5), 2926 for name, newval in (("hour", 5),
2711 ("minute", 6), 2927 ("minute", 6),
2712 ("second", 7), 2928 ("second", 7),
2713 ("microsecond", 8), 2929 ("microsecond", 8),
2714 ("tzinfo", zm200)): 2930 ("tzinfo", zm200)):
2715 newargs = args[:] 2931 newargs = args[:]
2716 newargs[i] = newval 2932 newargs[i] = newval
2717 expected = cls(*newargs) 2933 expected = cls(*newargs)
2718 got = base.replace(**{name: newval}) 2934 got = base.replace(**{name: newval})
2719 self.assertEqual(expected, got) 2935 self.assertEqual(expected, got)
2720 i += 1 2936 i += 1
2721 2937
2722 # Ensure we can get rid of a tzinfo. 2938 # Ensure we can get rid of a tzinfo.
2723 self.assertEqual(base.tzname(), "+100") 2939 self.assertEqual(base.tzname(), "+100")
2724 base2 = base.replace(tzinfo=None) 2940 base2 = base.replace(tzinfo=None)
2725 self.assertTrue(base2.tzinfo is None) 2941 self.assertIsNone(base2.tzinfo)
2726 self.assertTrue(base2.tzname() is None) 2942 self.assertIsNone(base2.tzname())
2727 2943
2728 # Ensure we can add one. 2944 # Ensure we can add one.
2729 base3 = base2.replace(tzinfo=z100) 2945 base3 = base2.replace(tzinfo=z100)
2730 self.assertEqual(base, base3) 2946 self.assertEqual(base, base3)
2731 self.assertTrue(base.tzinfo is base3.tzinfo) 2947 self.assertIs(base.tzinfo, base3.tzinfo)
2732 2948
2733 # Out of bounds. 2949 # Out of bounds.
2734 base = cls(1) 2950 base = cls(1)
2735 self.assertRaises(ValueError, base.replace, hour=24) 2951 self.assertRaises(ValueError, base.replace, hour=24)
2736 self.assertRaises(ValueError, base.replace, minute=-1) 2952 self.assertRaises(ValueError, base.replace, minute=-1)
2737 self.assertRaises(ValueError, base.replace, second=100) 2953 self.assertRaises(ValueError, base.replace, second=100)
2738 self.assertRaises(ValueError, base.replace, microsecond=1000000) 2954 self.assertRaises(ValueError, base.replace, microsecond=1000000)
2739 2955
2740 def test_mixed_compare(self): 2956 def test_mixed_compare(self):
2741 t1 = time(1, 2, 3) 2957 t1 = time(1, 2, 3)
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
2956 self.assertEqual(dt.time(), time(18, 45, 3, 1234)) 3172 self.assertEqual(dt.time(), time(18, 45, 3, 1234))
2957 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met)) 3173 self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
2958 3174
2959 def test_tz_aware_arithmetic(self): 3175 def test_tz_aware_arithmetic(self):
2960 import random 3176 import random
2961 3177
2962 now = self.theclass.now() 3178 now = self.theclass.now()
2963 tz55 = FixedOffset(-330, "west 5:30") 3179 tz55 = FixedOffset(-330, "west 5:30")
2964 timeaware = now.time().replace(tzinfo=tz55) 3180 timeaware = now.time().replace(tzinfo=tz55)
2965 nowaware = self.theclass.combine(now.date(), timeaware) 3181 nowaware = self.theclass.combine(now.date(), timeaware)
2966 self.assertTrue(nowaware.tzinfo is tz55) 3182 self.assertIs(nowaware.tzinfo, tz55)
2967 self.assertEqual(nowaware.timetz(), timeaware) 3183 self.assertEqual(nowaware.timetz(), timeaware)
2968 3184
2969 # Can't mix aware and non-aware. 3185 # Can't mix aware and non-aware.
2970 self.assertRaises(TypeError, lambda: now - nowaware) 3186 self.assertRaises(TypeError, lambda: now - nowaware)
2971 self.assertRaises(TypeError, lambda: nowaware - now) 3187 self.assertRaises(TypeError, lambda: nowaware - now)
2972 3188
2973 # And adding datetime's doesn't make sense, aware or not. 3189 # And adding datetime's doesn't make sense, aware or not.
2974 self.assertRaises(TypeError, lambda: now + nowaware) 3190 self.assertRaises(TypeError, lambda: now + nowaware)
2975 self.assertRaises(TypeError, lambda: nowaware + now) 3191 self.assertRaises(TypeError, lambda: nowaware + now)
2976 self.assertRaises(TypeError, lambda: nowaware + nowaware) 3192 self.assertRaises(TypeError, lambda: nowaware + nowaware)
2977 3193
2978 # Subtracting should yield 0. 3194 # Subtracting should yield 0.
2979 self.assertEqual(now - now, timedelta(0)) 3195 self.assertEqual(now - now, timedelta(0))
2980 self.assertEqual(nowaware - nowaware, timedelta(0)) 3196 self.assertEqual(nowaware - nowaware, timedelta(0))
2981 3197
2982 # Adding a delta should preserve tzinfo. 3198 # Adding a delta should preserve tzinfo.
2983 delta = timedelta(weeks=1, minutes=12, microseconds=5678) 3199 delta = timedelta(weeks=1, minutes=12, microseconds=5678)
2984 nowawareplus = nowaware + delta 3200 nowawareplus = nowaware + delta
2985 self.assertTrue(nowaware.tzinfo is tz55) 3201 self.assertIs(nowaware.tzinfo, tz55)
2986 nowawareplus2 = delta + nowaware 3202 nowawareplus2 = delta + nowaware
2987 self.assertTrue(nowawareplus2.tzinfo is tz55) 3203 self.assertIs(nowawareplus2.tzinfo, tz55)
2988 self.assertEqual(nowawareplus, nowawareplus2) 3204 self.assertEqual(nowawareplus, nowawareplus2)
2989 3205
2990 # that - delta should be what we started with, and that - what we 3206 # that - delta should be what we started with, and that - what we
2991 # started with should be delta. 3207 # started with should be delta.
2992 diff = nowawareplus - delta 3208 diff = nowawareplus - delta
2993 self.assertTrue(diff.tzinfo is tz55) 3209 self.assertIs(diff.tzinfo, tz55)
2994 self.assertEqual(nowaware, diff) 3210 self.assertEqual(nowaware, diff)
2995 self.assertRaises(TypeError, lambda: delta - nowawareplus) 3211 self.assertRaises(TypeError, lambda: delta - nowawareplus)
2996 self.assertEqual(nowawareplus - nowaware, delta) 3212 self.assertEqual(nowawareplus - nowaware, delta)
2997 3213
2998 # Make up a random timezone. 3214 # Make up a random timezone.
2999 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone") 3215 tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
3000 # Attach it to nowawareplus. 3216 # Attach it to nowawareplus.
3001 nowawareplus = nowawareplus.replace(tzinfo=tzr) 3217 nowawareplus = nowawareplus.replace(tzinfo=tzr)
3002 self.assertTrue(nowawareplus.tzinfo is tzr) 3218 self.assertIs(nowawareplus.tzinfo, tzr)
3003 # Make sure the difference takes the timezone adjustments into account. 3219 # Make sure the difference takes the timezone adjustments into account.
3004 got = nowaware - nowawareplus 3220 got = nowaware - nowawareplus
3005 # Expected: (nowaware base - nowaware offset) - 3221 # Expected: (nowaware base - nowaware offset) -
3006 # (nowawareplus base - nowawareplus offset) = 3222 # (nowawareplus base - nowawareplus offset) =
3007 # (nowaware base - nowawareplus base) + 3223 # (nowaware base - nowawareplus base) +
3008 # (nowawareplus offset - nowaware offset) = 3224 # (nowawareplus offset - nowaware offset) =
3009 # -delta + nowawareplus offset - nowaware offset 3225 # -delta + nowawareplus offset - nowaware offset
3010 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta 3226 expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
3011 self.assertEqual(got, expected) 3227 self.assertEqual(got, expected)
3012 3228
(...skipping 11 matching lines...) Expand all
3024 self.assertEqual(delta, self.theclass.min - self.theclass.max) 3240 self.assertEqual(delta, self.theclass.min - self.theclass.max)
3025 3241
3026 def test_tzinfo_now(self): 3242 def test_tzinfo_now(self):
3027 meth = self.theclass.now 3243 meth = self.theclass.now
3028 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up). 3244 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3029 base = meth() 3245 base = meth()
3030 # Try with and without naming the keyword. 3246 # Try with and without naming the keyword.
3031 off42 = FixedOffset(42, "42") 3247 off42 = FixedOffset(42, "42")
3032 another = meth(off42) 3248 another = meth(off42)
3033 again = meth(tz=off42) 3249 again = meth(tz=off42)
3034 self.assertTrue(another.tzinfo is again.tzinfo) 3250 self.assertIs(another.tzinfo, again.tzinfo)
3035 self.assertEqual(another.utcoffset(), timedelta(minutes=42)) 3251 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
3036 # Bad argument with and w/o naming the keyword. 3252 # Bad argument with and w/o naming the keyword.
3037 self.assertRaises(TypeError, meth, 16) 3253 self.assertRaises(TypeError, meth, 16)
3038 self.assertRaises(TypeError, meth, tzinfo=16) 3254 self.assertRaises(TypeError, meth, tzinfo=16)
3039 # Bad keyword name. 3255 # Bad keyword name.
3040 self.assertRaises(TypeError, meth, tinfo=off42) 3256 self.assertRaises(TypeError, meth, tinfo=off42)
3041 # Too many args. 3257 # Too many args.
3042 self.assertRaises(TypeError, meth, off42, off42) 3258 self.assertRaises(TypeError, meth, off42, off42)
3043 3259
3044 # We don't know which time zone we're in, and don't have a tzinfo 3260 # We don't know which time zone we're in, and don't have a tzinfo
3045 # class to represent it, so seeing whether a tz argument actually 3261 # class to represent it, so seeing whether a tz argument actually
3046 # does a conversion is tricky. 3262 # does a conversion is tricky.
3047 utc = FixedOffset(0, "utc", 0) 3263 utc = FixedOffset(0, "utc", 0)
3048 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0), 3264 for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
3049 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]: 3265 timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
3050 for dummy in range(3): 3266 for dummy in range(3):
3051 now = datetime.now(weirdtz) 3267 now = datetime.now(weirdtz)
3052 self.assertTrue(now.tzinfo is weirdtz) 3268 self.assertIs(now.tzinfo, weirdtz)
3053 utcnow = datetime.utcnow().replace(tzinfo=utc) 3269 utcnow = datetime.utcnow().replace(tzinfo=utc)
3054 now2 = utcnow.astimezone(weirdtz) 3270 now2 = utcnow.astimezone(weirdtz)
3055 if abs(now - now2) < timedelta(seconds=30): 3271 if abs(now - now2) < timedelta(seconds=30):
3056 break 3272 break
3057 # Else the code is broken, or more than 30 seconds passed betwee n 3273 # Else the code is broken, or more than 30 seconds passed betwee n
3058 # calls; assuming the latter, just try again. 3274 # calls; assuming the latter, just try again.
3059 else: 3275 else:
3060 # Three strikes and we're out. 3276 # Three strikes and we're out.
3061 self.fail("utcnow(), now(tz), or astimezone() may be broken") 3277 self.fail("utcnow(), now(tz), or astimezone() may be broken")
3062 3278
3063 def test_tzinfo_fromtimestamp(self): 3279 def test_tzinfo_fromtimestamp(self):
3064 import time 3280 import time
3065 meth = self.theclass.fromtimestamp 3281 meth = self.theclass.fromtimestamp
3066 ts = time.time() 3282 ts = time.time()
3067 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up). 3283 # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
3068 base = meth(ts) 3284 base = meth(ts)
3069 # Try with and without naming the keyword. 3285 # Try with and without naming the keyword.
3070 off42 = FixedOffset(42, "42") 3286 off42 = FixedOffset(42, "42")
3071 another = meth(ts, off42) 3287 another = meth(ts, off42)
3072 again = meth(ts, tz=off42) 3288 again = meth(ts, tz=off42)
3073 self.assertTrue(another.tzinfo is again.tzinfo) 3289 self.assertIs(another.tzinfo, again.tzinfo)
3074 self.assertEqual(another.utcoffset(), timedelta(minutes=42)) 3290 self.assertEqual(another.utcoffset(), timedelta(minutes=42))
3075 # Bad argument with and w/o naming the keyword. 3291 # Bad argument with and w/o naming the keyword.
3076 self.assertRaises(TypeError, meth, ts, 16) 3292 self.assertRaises(TypeError, meth, ts, 16)
3077 self.assertRaises(TypeError, meth, ts, tzinfo=16) 3293 self.assertRaises(TypeError, meth, ts, tzinfo=16)
3078 # Bad keyword name. 3294 # Bad keyword name.
3079 self.assertRaises(TypeError, meth, ts, tinfo=off42) 3295 self.assertRaises(TypeError, meth, ts, tinfo=off42)
3080 # Too many args. 3296 # Too many args.
3081 self.assertRaises(TypeError, meth, ts, off42, off42) 3297 self.assertRaises(TypeError, meth, ts, off42, off42)
3082 # Too few args. 3298 # Too few args.
3083 self.assertRaises(TypeError, meth) 3299 self.assertRaises(TypeError, meth)
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
3274 newargs = args[:] 3490 newargs = args[:]
3275 newargs[i] = newval 3491 newargs[i] = newval
3276 expected = cls(*newargs) 3492 expected = cls(*newargs)
3277 got = base.replace(**{name: newval}) 3493 got = base.replace(**{name: newval})
3278 self.assertEqual(expected, got) 3494 self.assertEqual(expected, got)
3279 i += 1 3495 i += 1
3280 3496
3281 # Ensure we can get rid of a tzinfo. 3497 # Ensure we can get rid of a tzinfo.
3282 self.assertEqual(base.tzname(), "+100") 3498 self.assertEqual(base.tzname(), "+100")
3283 base2 = base.replace(tzinfo=None) 3499 base2 = base.replace(tzinfo=None)
3284 self.assertTrue(base2.tzinfo is None) 3500 self.assertIsNone(base2.tzinfo)
3285 self.assertTrue(base2.tzname() is None) 3501 self.assertIsNone(base2.tzname())
3286 3502
3287 # Ensure we can add one. 3503 # Ensure we can add one.
3288 base3 = base2.replace(tzinfo=z100) 3504 base3 = base2.replace(tzinfo=z100)
3289 self.assertEqual(base, base3) 3505 self.assertEqual(base, base3)
3290 self.assertTrue(base.tzinfo is base3.tzinfo) 3506 self.assertIs(base.tzinfo, base3.tzinfo)
3291 3507
3292 # Out of bounds. 3508 # Out of bounds.
3293 base = cls(2000, 2, 29) 3509 base = cls(2000, 2, 29)
3294 self.assertRaises(ValueError, base.replace, year=2001) 3510 self.assertRaises(ValueError, base.replace, year=2001)
3295 3511
3296 def test_more_astimezone(self): 3512 def test_more_astimezone(self):
3297 # The inherited test_astimezone covered some trivial and error cases. 3513 # The inherited test_astimezone covered some trivial and error cases.
3298 fnone = FixedOffset(None, "None") 3514 fnone = FixedOffset(None, "None")
3299 f44m = FixedOffset(44, "44") 3515 f44m = FixedOffset(44, "44")
3300 fm5h = FixedOffset(-timedelta(hours=5), "m300") 3516 fm5h = FixedOffset(-timedelta(hours=5), "m300")
3301 3517
3302 dt = self.theclass.now(tz=f44m) 3518 dt = self.theclass.now(tz=f44m)
3303 self.assertTrue(dt.tzinfo is f44m) 3519 self.assertIs(dt.tzinfo, f44m)
3304 # Replacing with degenerate tzinfo raises an exception. 3520 # Replacing with degenerate tzinfo raises an exception.
3305 self.assertRaises(ValueError, dt.astimezone, fnone) 3521 self.assertRaises(ValueError, dt.astimezone, fnone)
3306 # Replacing with same tzinfo makes no change. 3522 # Replacing with same tzinfo makes no change.
3307 x = dt.astimezone(dt.tzinfo) 3523 x = dt.astimezone(dt.tzinfo)
3308 self.assertTrue(x.tzinfo is f44m) 3524 self.assertIs(x.tzinfo, f44m)
3309 self.assertEqual(x.date(), dt.date()) 3525 self.assertEqual(x.date(), dt.date())
3310 self.assertEqual(x.time(), dt.time()) 3526 self.assertEqual(x.time(), dt.time())
3311 3527
3312 # Replacing with different tzinfo does adjust. 3528 # Replacing with different tzinfo does adjust.
3313 got = dt.astimezone(fm5h) 3529 got = dt.astimezone(fm5h)
3314 self.assertTrue(got.tzinfo is fm5h) 3530 self.assertIs(got.tzinfo, fm5h)
3315 self.assertEqual(got.utcoffset(), timedelta(hours=-5)) 3531 self.assertEqual(got.utcoffset(), timedelta(hours=-5))
3316 expected = dt - dt.utcoffset() # in effect, convert to UTC 3532 expected = dt - dt.utcoffset() # in effect, convert to UTC
3317 expected += fm5h.utcoffset(dt) # and from there to local time 3533 expected += fm5h.utcoffset(dt) # and from there to local time
3318 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo 3534 expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
3319 self.assertEqual(got.date(), expected.date()) 3535 self.assertEqual(got.date(), expected.date())
3320 self.assertEqual(got.time(), expected.time()) 3536 self.assertEqual(got.time(), expected.time())
3321 self.assertEqual(got.timetz(), expected.timetz()) 3537 self.assertEqual(got.timetz(), expected.timetz())
3322 self.assertTrue(got.tzinfo is expected.tzinfo) 3538 self.assertIs(got.tzinfo, expected.tzinfo)
3323 self.assertEqual(got, expected) 3539 self.assertEqual(got, expected)
3324 3540
3325 @support.run_with_tz('UTC') 3541 @support.run_with_tz('UTC')
3326 def test_astimezone_default_utc(self): 3542 def test_astimezone_default_utc(self):
3327 dt = self.theclass.now(timezone.utc) 3543 dt = self.theclass.now(timezone.utc)
3328 self.assertEqual(dt.astimezone(None), dt) 3544 self.assertEqual(dt.astimezone(None), dt)
3329 self.assertEqual(dt.astimezone(), dt) 3545 self.assertEqual(dt.astimezone(), dt)
3330 3546
3331 # Note that offset in TZ variable has the opposite sign to that 3547 # Note that offset in TZ variable has the opposite sign to that
3332 # produced by %z directive. 3548 # produced by %z directive.
3333 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0') 3549 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
3334 def test_astimezone_default_eastern(self): 3550 def test_astimezone_default_eastern(self):
3335 dt = self.theclass(2012, 11, 4, 6, 30, tzinfo=timezone.utc) 3551 dt = self.theclass(2012, 11, 4, 6, 30, tzinfo=timezone.utc)
3336 local = dt.astimezone() 3552 local = dt.astimezone()
3337 self.assertEqual(dt, local) 3553 self.assertEqual(dt, local)
3338 self.assertEqual(local.strftime("%z %Z"), "-0500 EST") 3554 self.assertEqual(local.strftime("%z %Z"), "-0500 EST")
3339 dt = self.theclass(2012, 11, 4, 5, 30, tzinfo=timezone.utc) 3555 dt = self.theclass(2012, 11, 4, 5, 30, tzinfo=timezone.utc)
3340 local = dt.astimezone() 3556 local = dt.astimezone()
3341 self.assertEqual(dt, local) 3557 self.assertEqual(dt, local)
3342 self.assertEqual(local.strftime("%z %Z"), "-0400 EDT") 3558 self.assertEqual(local.strftime("%z %Z"), "-0400 EDT")
3559
3560 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
3561 def test_astimezone_default_near_fold(self):
3562 # Issue #26616.
3563 u = datetime(2015, 11, 1, 5, tzinfo=timezone.utc)
3564 t = u.astimezone()
3565 s = t.astimezone()
3566 self.assertEqual(t.tzinfo, s.tzinfo)
3343 3567
3344 def test_aware_subtract(self): 3568 def test_aware_subtract(self):
3345 cls = self.theclass 3569 cls = self.theclass
3346 3570
3347 # Ensure that utcoffset() is ignored when the operands have the 3571 # Ensure that utcoffset() is ignored when the operands have the
3348 # same tzinfo member. 3572 # same tzinfo member.
3349 class OperandDependentOffset(tzinfo): 3573 class OperandDependentOffset(tzinfo):
3350 def utcoffset(self, t): 3574 def utcoffset(self, t):
3351 if t.minute < 10: 3575 if t.minute < 10:
3352 # d0 and d1 equal after adjustment 3576 # d0 and d1 equal after adjustment
(...skipping 420 matching lines...) Expand 10 before | Expand all | Expand 10 after
3773 3997
3774 class Oddballs(unittest.TestCase): 3998 class Oddballs(unittest.TestCase):
3775 3999
3776 def test_bug_1028306(self): 4000 def test_bug_1028306(self):
3777 # Trying to compare a date to a datetime should act like a mixed- 4001 # Trying to compare a date to a datetime should act like a mixed-
3778 # type comparison, despite that datetime is a subclass of date. 4002 # type comparison, despite that datetime is a subclass of date.
3779 as_date = date.today() 4003 as_date = date.today()
3780 as_datetime = datetime.combine(as_date, time()) 4004 as_datetime = datetime.combine(as_date, time())
3781 self.assertTrue(as_date != as_datetime) 4005 self.assertTrue(as_date != as_datetime)
3782 self.assertTrue(as_datetime != as_date) 4006 self.assertTrue(as_datetime != as_date)
3783 self.assertTrue(not as_date == as_datetime) 4007 self.assertFalse(as_date == as_datetime)
3784 self.assertTrue(not as_datetime == as_date) 4008 self.assertFalse(as_datetime == as_date)
3785 self.assertRaises(TypeError, lambda: as_date < as_datetime) 4009 self.assertRaises(TypeError, lambda: as_date < as_datetime)
3786 self.assertRaises(TypeError, lambda: as_datetime < as_date) 4010 self.assertRaises(TypeError, lambda: as_datetime < as_date)
3787 self.assertRaises(TypeError, lambda: as_date <= as_datetime) 4011 self.assertRaises(TypeError, lambda: as_date <= as_datetime)
3788 self.assertRaises(TypeError, lambda: as_datetime <= as_date) 4012 self.assertRaises(TypeError, lambda: as_datetime <= as_date)
3789 self.assertRaises(TypeError, lambda: as_date > as_datetime) 4013 self.assertRaises(TypeError, lambda: as_date > as_datetime)
3790 self.assertRaises(TypeError, lambda: as_datetime > as_date) 4014 self.assertRaises(TypeError, lambda: as_datetime > as_date)
3791 self.assertRaises(TypeError, lambda: as_date >= as_datetime) 4015 self.assertRaises(TypeError, lambda: as_date >= as_datetime)
3792 self.assertRaises(TypeError, lambda: as_datetime >= as_date) 4016 self.assertRaises(TypeError, lambda: as_datetime >= as_date)
3793 4017
3794 # Neverthelss, comparison should work with the base-class (date) 4018 # Neverthelss, comparison should work with the base-class (date)
3795 # projection if use of a date method is forced. 4019 # projection if use of a date method is forced.
3796 self.assertEqual(as_date.__eq__(as_datetime), True) 4020 self.assertEqual(as_date.__eq__(as_datetime), True)
3797 different_day = (as_date.day + 1) % 20 + 1 4021 different_day = (as_date.day + 1) % 20 + 1
3798 as_different = as_datetime.replace(day= different_day) 4022 as_different = as_datetime.replace(day= different_day)
3799 self.assertEqual(as_date.__eq__(as_different), False) 4023 self.assertEqual(as_date.__eq__(as_different), False)
3800 4024
3801 # And date should compare with other subclasses of date. If a 4025 # And date should compare with other subclasses of date. If a
3802 # subclass wants to stop this, it's up to the subclass to do so. 4026 # subclass wants to stop this, it's up to the subclass to do so.
3803 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day) 4027 date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
3804 self.assertEqual(as_date, date_sc) 4028 self.assertEqual(as_date, date_sc)
3805 self.assertEqual(date_sc, as_date) 4029 self.assertEqual(date_sc, as_date)
3806 4030
3807 # Ditto for datetimes. 4031 # Ditto for datetimes.
3808 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month, 4032 datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
3809 as_date.day, 0, 0, 0) 4033 as_date.day, 0, 0, 0)
3810 self.assertEqual(as_datetime, datetime_sc) 4034 self.assertEqual(as_datetime, datetime_sc)
3811 self.assertEqual(datetime_sc, as_datetime) 4035 self.assertEqual(datetime_sc, as_datetime)
3812 4036
3813 def test_main(): 4037 def test_extra_attributes(self):
3814 support.run_unittest(__name__) 4038 for x in [date.today(),
4039 time(),
4040 datetime.utcnow(),
4041 timedelta(),
4042 tzinfo(),
4043 timezone(timedelta())]:
4044 with self.assertRaises(AttributeError):
4045 x.abc = 1
4046
4047 def test_check_arg_types(self):
4048 class Number:
4049 def __init__(self, value):
4050 self.value = value
4051 def __int__(self):
4052 return self.value
4053
4054 for xx in [decimal.Decimal(10),
4055 decimal.Decimal('10.9'),
4056 Number(10)]:
4057 self.assertEqual(datetime(10, 10, 10, 10, 10, 10, 10),
4058 datetime(xx, xx, xx, xx, xx, xx, xx))
4059
4060 with self.assertRaisesRegex(TypeError, '^an integer is required '
4061 '\(got type str\)$'):
4062 datetime(10, 10, '10')
4063
4064 f10 = Number(10.9)
4065 with self.assertRaisesRegex(TypeError, '^__int__ returned non-int '
4066 '\(type float\)$'):
4067 datetime(10, 10, f10)
4068
4069 class Float(float):
4070 pass
4071 s10 = Float(10.9)
4072 with self.assertRaisesRegex(TypeError, '^integer argument expected, '
4073 'got float$'):
4074 datetime(10, 10, s10)
4075
4076 with self.assertRaises(TypeError):
4077 datetime(10., 10, 10)
4078 with self.assertRaises(TypeError):
4079 datetime(10, 10., 10)
4080 with self.assertRaises(TypeError):
4081 datetime(10, 10, 10.)
4082 with self.assertRaises(TypeError):
4083 datetime(10, 10, 10, 10.)
4084 with self.assertRaises(TypeError):
4085 datetime(10, 10, 10, 10, 10.)
4086 with self.assertRaises(TypeError):
4087 datetime(10, 10, 10, 10, 10, 10.)
4088 with self.assertRaises(TypeError):
4089 datetime(10, 10, 10, 10, 10, 10, 10.)
4090
4091 #############################################################################
4092 # Local Time Disambiguation
4093
4094 # An experimental reimplementation of fromutc that respects the "fold" flag.
4095
4096 class tzinfo2(tzinfo):
4097
4098 def fromutc(self, dt):
4099 "datetime in UTC -> datetime in local time."
4100
4101 if not isinstance(dt, datetime):
4102 raise TypeError("fromutc() requires a datetime argument")
4103 if dt.tzinfo is not self:
4104 raise ValueError("dt.tzinfo is not self")
4105 # Returned value satisfies
4106 # dt + ldt.utcoffset() = ldt
4107 off0 = dt.replace(fold=0).utcoffset()
4108 off1 = dt.replace(fold=1).utcoffset()
4109 if off0 is None or off1 is None or dt.dst() is None:
4110 raise ValueError
4111 if off0 == off1:
4112 ldt = dt + off0
4113 off1 = ldt.utcoffset()
4114 if off0 == off1:
4115 return ldt
4116 # Now, we discovered both possible offsets, so
4117 # we can just try four possible solutions:
4118 for off in [off0, off1]:
4119 ldt = dt + off
4120 if ldt.utcoffset() == off:
4121 return ldt
4122 ldt = ldt.replace(fold=1)
4123 if ldt.utcoffset() == off:
4124 return ldt
4125
4126 raise ValueError("No suitable local time found")
4127
4128 # Reimplementing simplified US timezones to respect the "fold" flag:
4129
4130 class USTimeZone2(tzinfo2):
4131
4132 def __init__(self, hours, reprname, stdname, dstname):
4133 self.stdoffset = timedelta(hours=hours)
4134 self.reprname = reprname
4135 self.stdname = stdname
4136 self.dstname = dstname
4137
4138 def __repr__(self):
4139 return self.reprname
4140
4141 def tzname(self, dt):
4142 if self.dst(dt):
4143 return self.dstname
4144 else:
4145 return self.stdname
4146
4147 def utcoffset(self, dt):
4148 return self.stdoffset + self.dst(dt)
4149
4150 def dst(self, dt):
4151 if dt is None or dt.tzinfo is None:
4152 # An exception instead may be sensible here, in one or more of
4153 # the cases.
4154 return ZERO
4155 assert dt.tzinfo is self
4156
4157 # Find first Sunday in April.
4158 start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
4159 assert start.weekday() == 6 and start.month == 4 and start.day <= 7
4160
4161 # Find last Sunday in October.
4162 end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
4163 assert end.weekday() == 6 and end.month == 10 and end.day >= 25
4164
4165 # Can't compare naive to aware objects, so strip the timezone from
4166 # dt first.
4167 dt = dt.replace(tzinfo=None)
4168 if start + HOUR <= dt < end:
4169 # DST is in effect.
4170 return HOUR
4171 elif end <= dt < end + HOUR:
4172 # Fold (an ambiguous hour): use dt.fold to disambiguate.
4173 return ZERO if dt.fold else HOUR
4174 elif start <= dt < start + HOUR:
4175 # Gap (a non-existent hour): reverse the fold rule.
4176 return HOUR if dt.fold else ZERO
4177 else:
4178 # DST is off.
4179 return ZERO
4180
4181 Eastern2 = USTimeZone2(-5, "Eastern2", "EST", "EDT")
4182 Central2 = USTimeZone2(-6, "Central2", "CST", "CDT")
4183 Mountain2 = USTimeZone2(-7, "Mountain2", "MST", "MDT")
4184 Pacific2 = USTimeZone2(-8, "Pacific2", "PST", "PDT")
4185
4186 # Europe_Vilnius_1941 tzinfo implementation reproduces the following
4187 # 1941 transition from Olson's tzdist:
4188 #
4189 # Zone NAME GMTOFF RULES FORMAT [UNTIL]
4190 # ZoneEurope/Vilnius 1:00 - CET 1940 Aug 3
4191 # 3:00 - MSK 1941 Jun 24
4192 # 1:00 C-Eur CE%sT 1944 Aug
4193 #
4194 # $ zdump -v Europe/Vilnius | grep 1941
4195 # Europe/Vilnius Mon Jun 23 20:59:59 1941 UTC = Mon Jun 23 23:59:59 1941 MSK is dst=0 gmtoff=10800
4196 # Europe/Vilnius Mon Jun 23 21:00:00 1941 UTC = Mon Jun 23 23:00:00 1941 CEST i sdst=1 gmtoff=7200
4197
4198 class Europe_Vilnius_1941(tzinfo):
4199 def _utc_fold(self):
4200 return [datetime(1941, 6, 23, 21, tzinfo=self), # Mon Jun 23 21:00:00 1 941 UTC
4201 datetime(1941, 6, 23, 22, tzinfo=self)] # Mon Jun 23 22:00:00 1 941 UTC
4202
4203 def _loc_fold(self):
4204 return [datetime(1941, 6, 23, 23, tzinfo=self), # Mon Jun 23 23:00:00 1 941 MSK / CEST
4205 datetime(1941, 6, 24, 0, tzinfo=self)] # Mon Jun 24 00:00:00 1 941 CEST
4206
4207 def utcoffset(self, dt):
4208 fold_start, fold_stop = self._loc_fold()
4209 if dt < fold_start:
4210 return 3 * HOUR
4211 if dt < fold_stop:
4212 return (2 if dt.fold else 3) * HOUR
4213 # if dt >= fold_stop
4214 return 2 * HOUR
4215
4216 def dst(self, dt):
4217 fold_start, fold_stop = self._loc_fold()
4218 if dt < fold_start:
4219 return 0 * HOUR
4220 if dt < fold_stop:
4221 return (1 if dt.fold else 0) * HOUR
4222 # if dt >= fold_stop
4223 return 1 * HOUR
4224
4225 def tzname(self, dt):
4226 fold_start, fold_stop = self._loc_fold()
4227 if dt < fold_start:
4228 return 'MSK'
4229 if dt < fold_stop:
4230 return ('MSK', 'CEST')[dt.fold]
4231 # if dt >= fold_stop
4232 return 'CEST'
4233
4234 def fromutc(self, dt):
4235 assert dt.fold == 0
4236 assert dt.tzinfo is self
4237 if dt.year != 1941:
4238 raise NotImplementedError
4239 fold_start, fold_stop = self._utc_fold()
4240 if dt < fold_start:
4241 return dt + 3 * HOUR
4242 if dt < fold_stop:
4243 return (dt + 2 * HOUR).replace(fold=1)
4244 # if dt >= fold_stop
4245 return dt + 2 * HOUR
4246
4247
4248 class TestLocalTimeDisambiguation(unittest.TestCase):
4249
4250 def test_vilnius_1941_fromutc(self):
4251 Vilnius = Europe_Vilnius_1941()
4252
4253 gdt = datetime(1941, 6, 23, 20, 59, 59, tzinfo=timezone.utc)
4254 ldt = gdt.astimezone(Vilnius)
4255 self.assertEqual(ldt.strftime("%c %Z%z"),
4256 'Mon Jun 23 23:59:59 1941 MSK+0300')
4257 self.assertEqual(ldt.fold, 0)
4258 self.assertFalse(ldt.dst())
4259
4260 gdt = datetime(1941, 6, 23, 21, tzinfo=timezone.utc)
4261 ldt = gdt.astimezone(Vilnius)
4262 self.assertEqual(ldt.strftime("%c %Z%z"),
4263 'Mon Jun 23 23:00:00 1941 CEST+0200')
4264 self.assertEqual(ldt.fold, 1)
4265 self.assertTrue(ldt.dst())
4266
4267 gdt = datetime(1941, 6, 23, 22, tzinfo=timezone.utc)
4268 ldt = gdt.astimezone(Vilnius)
4269 self.assertEqual(ldt.strftime("%c %Z%z"),
4270 'Tue Jun 24 00:00:00 1941 CEST+0200')
4271 self.assertEqual(ldt.fold, 0)
4272 self.assertTrue(ldt.dst())
4273
4274 def test_vilnius_1941_toutc(self):
4275 Vilnius = Europe_Vilnius_1941()
4276
4277 ldt = datetime(1941, 6, 23, 22, 59, 59, tzinfo=Vilnius)
4278 gdt = ldt.astimezone(timezone.utc)
4279 self.assertEqual(gdt.strftime("%c %Z"),
4280 'Mon Jun 23 19:59:59 1941 UTC')
4281
4282 ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius)
4283 gdt = ldt.astimezone(timezone.utc)
4284 self.assertEqual(gdt.strftime("%c %Z"),
4285 'Mon Jun 23 20:59:59 1941 UTC')
4286
4287 ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius, fold=1)
4288 gdt = ldt.astimezone(timezone.utc)
4289 self.assertEqual(gdt.strftime("%c %Z"),
4290 'Mon Jun 23 21:59:59 1941 UTC')
4291
4292 ldt = datetime(1941, 6, 24, 0, tzinfo=Vilnius)
4293 gdt = ldt.astimezone(timezone.utc)
4294 self.assertEqual(gdt.strftime("%c %Z"),
4295 'Mon Jun 23 22:00:00 1941 UTC')
4296
4297
4298 def test_constructors(self):
4299 t = time(0, fold=1)
4300 dt = datetime(1, 1, 1, fold=1)
4301 self.assertEqual(t.fold, 1)
4302 self.assertEqual(dt.fold, 1)
4303 with self.assertRaises(TypeError):
4304 time(0, 0, 0, 0, None, 0)
4305
4306 def test_member(self):
4307 dt = datetime(1, 1, 1, fold=1)
4308 t = dt.time()
4309 self.assertEqual(t.fold, 1)
4310 t = dt.timetz()
4311 self.assertEqual(t.fold, 1)
4312
4313 def test_replace(self):
4314 t = time(0)
4315 dt = datetime(1, 1, 1)
4316 self.assertEqual(t.replace(fold=1).fold, 1)
4317 self.assertEqual(dt.replace(fold=1).fold, 1)
4318 self.assertEqual(t.replace(fold=0).fold, 0)
4319 self.assertEqual(dt.replace(fold=0).fold, 0)
4320 # Check that replacement of other fields does not change "fold".
4321 t = t.replace(fold=1, tzinfo=Eastern)
4322 dt = dt.replace(fold=1, tzinfo=Eastern)
4323 self.assertEqual(t.replace(tzinfo=None).fold, 1)
4324 self.assertEqual(dt.replace(tzinfo=None).fold, 1)
4325 # Check that fold is a keyword-only argument
4326 with self.assertRaises(TypeError):
4327 t.replace(1, 1, 1, None, 1)
4328 with self.assertRaises(TypeError):
4329 dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1)
4330
4331 def test_comparison(self):
4332 t = time(0)
4333 dt = datetime(1, 1, 1)
4334 self.assertEqual(t, t.replace(fold=1))
4335 self.assertEqual(dt, dt.replace(fold=1))
4336
4337 def test_hash(self):
4338 t = time(0)
4339 dt = datetime(1, 1, 1)
4340 self.assertEqual(hash(t), hash(t.replace(fold=1)))
4341 self.assertEqual(hash(dt), hash(dt.replace(fold=1)))
4342
4343 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
4344 def test_fromtimestamp(self):
4345 s = 1414906200
4346 dt0 = datetime.fromtimestamp(s)
4347 dt1 = datetime.fromtimestamp(s + 3600)
4348 self.assertEqual(dt0.fold, 0)
4349 self.assertEqual(dt1.fold, 1)
4350
4351 @support.run_with_tz('Australia/Lord_Howe')
4352 def test_fromtimestamp_lord_howe(self):
4353 tm = _time.localtime(1.4e9)
4354 if _time.strftime('%Z%z', tm) != 'LHST+1030':
4355 self.skipTest('Australia/Lord_Howe timezone is not supported on this platform')
4356 # $ TZ=Australia/Lord_Howe date -r 1428158700
4357 # Sun Apr 5 01:45:00 LHDT 2015
4358 # $ TZ=Australia/Lord_Howe date -r 1428160500
4359 # Sun Apr 5 01:45:00 LHST 2015
4360 s = 1428158700
4361 t0 = datetime.fromtimestamp(s)
4362 t1 = datetime.fromtimestamp(s + 1800)
4363 self.assertEqual(t0, t1)
4364 self.assertEqual(t0.fold, 0)
4365 self.assertEqual(t1.fold, 1)
4366
4367
4368 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
4369 def test_timestamp(self):
4370 dt0 = datetime(2014, 11, 2, 1, 30)
4371 dt1 = dt0.replace(fold=1)
4372 self.assertEqual(dt0.timestamp() + 3600,
4373 dt1.timestamp())
4374
4375 @support.run_with_tz('Australia/Lord_Howe')
4376 def test_timestamp_lord_howe(self):
4377 tm = _time.localtime(1.4e9)
4378 if _time.strftime('%Z%z', tm) != 'LHST+1030':
4379 self.skipTest('Australia/Lord_Howe timezone is not supported on this platform')
4380 t = datetime(2015, 4, 5, 1, 45)
4381 s0 = t.replace(fold=0).timestamp()
4382 s1 = t.replace(fold=1).timestamp()
4383 self.assertEqual(s0 + 1800, s1)
4384
4385
4386 @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
4387 def test_astimezone(self):
4388 dt0 = datetime(2014, 11, 2, 1, 30)
4389 dt1 = dt0.replace(fold=1)
4390 # Convert both naive instances to aware.
4391 adt0 = dt0.astimezone()
4392 adt1 = dt1.astimezone()
4393 # Check that the first instance in DST zone and the second in STD
4394 self.assertEqual(adt0.tzname(), 'EDT')
4395 self.assertEqual(adt1.tzname(), 'EST')
4396 self.assertEqual(adt0 + HOUR, adt1)
4397 # Aware instances with fixed offset tzinfo's always have fold=0
4398 self.assertEqual(adt0.fold, 0)
4399 self.assertEqual(adt1.fold, 0)
4400
4401
4402 def test_pickle_fold(self):
4403 t = time(fold=1)
4404 dt = datetime(1, 1, 1, fold=1)
4405 for pickler, unpickler, proto in pickle_choices:
4406 for x in [t, dt]:
4407 s = pickler.dumps(x, proto)
4408 y = unpickler.loads(s)
4409 self.assertEqual(x, y)
4410 self.assertEqual((0 if proto < 4 else x.fold), y.fold)
4411
4412 def test_repr(self):
4413 t = time(fold=1)
4414 dt = datetime(1, 1, 1, fold=1)
4415 self.assertEqual(repr(t), 'datetime.time(0, 0, fold=1)')
4416 self.assertEqual(repr(dt),
4417 'datetime.datetime(1, 1, 1, 0, 0, fold=1)')
4418
4419 def test_dst(self):
4420 # Let's first establish that things work in regular times.
4421 dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resol ution
4422 dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2)
4423 self.assertEqual(dt_summer.dst(), HOUR)
4424 self.assertEqual(dt_winter.dst(), ZERO)
4425 # The disambiguation flag is ignored
4426 self.assertEqual(dt_summer.replace(fold=1).dst(), HOUR)
4427 self.assertEqual(dt_winter.replace(fold=1).dst(), ZERO)
4428
4429 # Pick local time in the fold.
4430 for minute in [0, 30, 59]:
4431 dt = datetime(2002, 10, 27, 1, minute, tzinfo=Eastern2)
4432 # With fold=0 (the default) it is in DST.
4433 self.assertEqual(dt.dst(), HOUR)
4434 # With fold=1 it is in STD.
4435 self.assertEqual(dt.replace(fold=1).dst(), ZERO)
4436
4437 # Pick local time in the gap.
4438 for minute in [0, 30, 59]:
4439 dt = datetime(2002, 4, 7, 2, minute, tzinfo=Eastern2)
4440 # With fold=0 (the default) it is in STD.
4441 self.assertEqual(dt.dst(), ZERO)
4442 # With fold=1 it is in DST.
4443 self.assertEqual(dt.replace(fold=1).dst(), HOUR)
4444
4445
4446 def test_utcoffset(self):
4447 # Let's first establish that things work in regular times.
4448 dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resol ution
4449 dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2)
4450 self.assertEqual(dt_summer.utcoffset(), -4 * HOUR)
4451 self.assertEqual(dt_winter.utcoffset(), -5 * HOUR)
4452 # The disambiguation flag is ignored
4453 self.assertEqual(dt_summer.replace(fold=1).utcoffset(), -4 * HOUR)
4454 self.assertEqual(dt_winter.replace(fold=1).utcoffset(), -5 * HOUR)
4455
4456 def test_fromutc(self):
4457 # Let's first establish that things work in regular times.
4458 u_summer = datetime(2002, 10, 27, 6, tzinfo=Eastern2) - timedelta.resolu tion
4459 u_winter = datetime(2002, 10, 27, 7, tzinfo=Eastern2)
4460 t_summer = Eastern2.fromutc(u_summer)
4461 t_winter = Eastern2.fromutc(u_winter)
4462 self.assertEqual(t_summer, u_summer - 4 * HOUR)
4463 self.assertEqual(t_winter, u_winter - 5 * HOUR)
4464 self.assertEqual(t_summer.fold, 0)
4465 self.assertEqual(t_winter.fold, 0)
4466
4467 # What happens in the fall-back fold?
4468 u = datetime(2002, 10, 27, 5, 30, tzinfo=Eastern2)
4469 t0 = Eastern2.fromutc(u)
4470 u += HOUR
4471 t1 = Eastern2.fromutc(u)
4472 self.assertEqual(t0, t1)
4473 self.assertEqual(t0.fold, 0)
4474 self.assertEqual(t1.fold, 1)
4475 # The tricky part is when u is in the local fold:
4476 u = datetime(2002, 10, 27, 1, 30, tzinfo=Eastern2)
4477 t = Eastern2.fromutc(u)
4478 self.assertEqual((t.day, t.hour), (26, 21))
4479 # .. or gets into the local fold after a standard time adjustment
4480 u = datetime(2002, 10, 27, 6, 30, tzinfo=Eastern2)
4481 t = Eastern2.fromutc(u)
4482 self.assertEqual((t.day, t.hour), (27, 1))
4483
4484 # What happens in the spring-forward gap?
4485 u = datetime(2002, 4, 7, 2, 0, tzinfo=Eastern2)
4486 t = Eastern2.fromutc(u)
4487 self.assertEqual((t.day, t.hour), (6, 21))
4488
4489 def test_mixed_compare_regular(self):
4490 t = datetime(2000, 1, 1, tzinfo=Eastern2)
4491 self.assertEqual(t, t.astimezone(timezone.utc))
4492 t = datetime(2000, 6, 1, tzinfo=Eastern2)
4493 self.assertEqual(t, t.astimezone(timezone.utc))
4494
4495 def test_mixed_compare_fold(self):
4496 t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2)
4497 t_fold_utc = t_fold.astimezone(timezone.utc)
4498 self.assertNotEqual(t_fold, t_fold_utc)
4499
4500 def test_mixed_compare_gap(self):
4501 t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2)
4502 t_gap_utc = t_gap.astimezone(timezone.utc)
4503 self.assertNotEqual(t_gap, t_gap_utc)
4504
4505 def test_hash_aware(self):
4506 t = datetime(2000, 1, 1, tzinfo=Eastern2)
4507 self.assertEqual(hash(t), hash(t.replace(fold=1)))
4508 t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2)
4509 t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2)
4510 self.assertEqual(hash(t_fold), hash(t_fold.replace(fold=1)))
4511 self.assertEqual(hash(t_gap), hash(t_gap.replace(fold=1)))
4512
4513 SEC = timedelta(0, 1)
4514
4515 def pairs(iterable):
4516 a, b = itertools.tee(iterable)
4517 next(b, None)
4518 return zip(a, b)
4519
4520 class ZoneInfo(tzinfo):
4521 zoneroot = '/usr/share/zoneinfo'
4522 def __init__(self, ut, ti):
4523 """
4524
4525 :param ut: array
4526 Array of transition point timestamps
4527 :param ti: list
4528 A list of (offset, isdst, abbr) tuples
4529 :return: None
4530 """
4531 self.ut = ut
4532 self.ti = ti
4533 self.lt = self.invert(ut, ti)
4534
4535 @staticmethod
4536 def invert(ut, ti):
4537 lt = (ut.__copy__(), ut.__copy__())
4538 if ut:
4539 offset = ti[0][0] // SEC
4540 lt[0][0] = max(-2**31, lt[0][0] + offset)
4541 lt[1][0] = max(-2**31, lt[1][0] + offset)
4542 for i in range(1, len(ut)):
4543 lt[0][i] += ti[i-1][0] // SEC
4544 lt[1][i] += ti[i][0] // SEC
4545 return lt
4546
4547 @classmethod
4548 def fromfile(cls, fileobj):
4549 if fileobj.read(4).decode() != "TZif":
4550 raise ValueError("not a zoneinfo file")
4551 fileobj.seek(32)
4552 counts = array('i')
4553 counts.fromfile(fileobj, 3)
4554 if sys.byteorder != 'big':
4555 counts.byteswap()
4556
4557 ut = array('i')
4558 ut.fromfile(fileobj, counts[0])
4559 if sys.byteorder != 'big':
4560 ut.byteswap()
4561
4562 type_indices = array('B')
4563 type_indices.fromfile(fileobj, counts[0])
4564
4565 ttis = []
4566 for i in range(counts[1]):
4567 ttis.append(struct.unpack(">lbb", fileobj.read(6)))
4568
4569 abbrs = fileobj.read(counts[2])
4570
4571 # Convert ttis
4572 for i, (gmtoff, isdst, abbrind) in enumerate(ttis):
4573 abbr = abbrs[abbrind:abbrs.find(0, abbrind)].decode()
4574 ttis[i] = (timedelta(0, gmtoff), isdst, abbr)
4575
4576 ti = [None] * len(ut)
4577 for i, idx in enumerate(type_indices):
4578 ti[i] = ttis[idx]
4579
4580 self = cls(ut, ti)
4581
4582 return self
4583
4584 @classmethod
4585 def fromname(cls, name):
4586 path = os.path.join(cls.zoneroot, name)
4587 with open(path, 'rb') as f:
4588 return cls.fromfile(f)
4589
4590 EPOCHORDINAL = date(1970, 1, 1).toordinal()
4591
4592 def fromutc(self, dt):
4593 """datetime in UTC -> datetime in local time."""
4594
4595 if not isinstance(dt, datetime):
4596 raise TypeError("fromutc() requires a datetime argument")
4597 if dt.tzinfo is not self:
4598 raise ValueError("dt.tzinfo is not self")
4599
4600 timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400
4601 + dt.hour * 3600
4602 + dt.minute * 60
4603 + dt.second)
4604
4605 if timestamp < self.ut[1]:
4606 tti = self.ti[0]
4607 fold = 0
4608 else:
4609 idx = bisect.bisect_right(self.ut, timestamp)
4610 assert self.ut[idx-1] <= timestamp
4611 assert idx == len(self.ut) or timestamp < self.ut[idx]
4612 tti_prev, tti = self.ti[idx-2:idx]
4613 # Detect fold
4614 shift = tti_prev[0] - tti[0]
4615 fold = (shift > timedelta(0, timestamp - self.ut[idx-1]))
4616 dt += tti[0]
4617 if fold:
4618 return dt.replace(fold=1)
4619 else:
4620 return dt
4621
4622 def _find_ti(self, dt, i):
4623 timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400
4624 + dt.hour * 3600
4625 + dt.minute * 60
4626 + dt.second)
4627 lt = self.lt[dt.fold]
4628 idx = bisect.bisect_right(lt, timestamp)
4629
4630 return self.ti[max(0, idx - 1)][i]
4631
4632 def utcoffset(self, dt):
4633 return self._find_ti(dt, 0)
4634
4635 def dst(self, dt):
4636 isdst = self._find_ti(dt, 1)
4637 # XXX: We cannot accurately determine the "save" value,
4638 # so let's return 1h whenever DST is in effect. Since
4639 # we don't use dst() in fromutc(), it is unlikely that
4640 # it will be needed for anything more than bool(dst()).
4641 return ZERO if isdst else HOUR
4642
4643 def tzname(self, dt):
4644 return self._find_ti(dt, 2)
4645
4646 @classmethod
4647 def zonenames(cls, zonedir=None):
4648 if zonedir is None:
4649 zonedir = cls.zoneroot
4650 for root, _, files in os.walk(zonedir):
4651 for f in files:
4652 p = os.path.join(root, f)
4653 with open(p, 'rb') as o:
4654 magic = o.read(4)
4655 if magic == b'TZif':
4656 yield p[len(zonedir) + 1:]
4657
4658 @classmethod
4659 def stats(cls, start_year=1):
4660 count = gap_count = fold_count = zeros_count = 0
4661 min_gap = min_fold = timedelta.max
4662 max_gap = max_fold = ZERO
4663 min_gap_datetime = max_gap_datetime = datetime.min
4664 min_gap_zone = max_gap_zone = None
4665 min_fold_datetime = max_fold_datetime = datetime.min
4666 min_fold_zone = max_fold_zone = None
4667 stats_since = datetime(start_year, 1, 1) # Starting from 1970 eliminates a lot of noise
4668 for zonename in cls.zonenames():
4669 count += 1
4670 tz = cls.fromname(zonename)
4671 for dt, shift in tz.transitions():
4672 if dt < stats_since:
4673 continue
4674 if shift > ZERO:
4675 gap_count += 1
4676 if (shift, dt) > (max_gap, max_gap_datetime):
4677 max_gap = shift
4678 max_gap_zone = zonename
4679 max_gap_datetime = dt
4680 if (shift, datetime.max - dt) < (min_gap, datetime.max - min _gap_datetime):
4681 min_gap = shift
4682 min_gap_zone = zonename
4683 min_gap_datetime = dt
4684 elif shift < ZERO:
4685 fold_count += 1
4686 shift = -shift
4687 if (shift, dt) > (max_fold, max_fold_datetime):
4688 max_fold = shift
4689 max_fold_zone = zonename
4690 max_fold_datetime = dt
4691 if (shift, datetime.max - dt) < (min_fold, datetime.max - mi n_fold_datetime):
4692 min_fold = shift
4693 min_fold_zone = zonename
4694 min_fold_datetime = dt
4695 else:
4696 zeros_count += 1
4697 trans_counts = (gap_count, fold_count, zeros_count)
4698 print("Number of zones: %5d" % count)
4699 print("Number of transitions: %5d = %d (gaps) + %d (folds) + %d (zeros)" %
4700 ((sum(trans_counts),) + trans_counts))
4701 print("Min gap: %16s at %s in %s" % (min_gap, min_gap_datetime, min_gap_zone))
4702 print("Max gap: %16s at %s in %s" % (max_gap, max_gap_datetime, max_gap_zone))
4703 print("Min fold: %16s at %s in %s" % (min_fold, min_fold_datetime , min_fold_zone))
4704 print("Max fold: %16s at %s in %s" % (max_fold, max_fold_datetime , max_fold_zone))
4705
4706
4707 def transitions(self):
4708 for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
4709 shift = ti[0] - prev_ti[0]
4710 yield datetime.utcfromtimestamp(t), shift
4711
4712 def nondst_folds(self):
4713 """Find all folds with the same value of isdst on both sides of the tran sition."""
4714 for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
4715 shift = ti[0] - prev_ti[0]
4716 if shift < ZERO and ti[1] == prev_ti[1]:
4717 yield datetime.utcfromtimestamp(t), -shift, prev_ti[2], ti[2]
4718
4719 @classmethod
4720 def print_all_nondst_folds(cls, same_abbr=False, start_year=1):
4721 count = 0
4722 for zonename in cls.zonenames():
4723 tz = cls.fromname(zonename)
4724 for dt, shift, prev_abbr, abbr in tz.nondst_folds():
4725 if dt.year < start_year or same_abbr and prev_abbr != abbr:
4726 continue
4727 count += 1
4728 print("%3d) %-30s %s %10s %5s -> %s" %
4729 (count, zonename, dt, shift, prev_abbr, abbr))
4730
4731 def folds(self):
4732 for t, shift in self.transitions():
4733 if shift < ZERO:
4734 yield t, -shift
4735
4736 def gaps(self):
4737 for t, shift in self.transitions():
4738 if shift > ZERO:
4739 yield t, shift
4740
4741 def zeros(self):
4742 for t, shift in self.transitions():
4743 if not shift:
4744 yield t
4745
4746
4747 class ZoneInfoTest(unittest.TestCase):
4748 zonename = 'America/New_York'
4749
4750 def setUp(self):
4751 self.sizeof_time_t = sysconfig.get_config_var('SIZEOF_TIME_T')
4752 if sys.platform == "win32":
4753 self.skipTest("Skipping zoneinfo tests on Windows")
4754 try:
4755 self.tz = ZoneInfo.fromname(self.zonename)
4756 except FileNotFoundError as err:
4757 self.skipTest("Skipping %s: %s" % (self.zonename, err))
4758
4759 def assertEquivDatetimes(self, a, b):
4760 self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)),
4761 (b.replace(tzinfo=None), b.fold, id(b.tzinfo)))
4762
4763 def test_folds(self):
4764 tz = self.tz
4765 for dt, shift in tz.folds():
4766 for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]:
4767 udt = dt + x
4768 ldt = tz.fromutc(udt.replace(tzinfo=tz))
4769 self.assertEqual(ldt.fold, 1)
4770 adt = udt.replace(tzinfo=timezone.utc).astimezone(tz)
4771 self.assertEquivDatetimes(adt, ldt)
4772 utcoffset = ldt.utcoffset()
4773 self.assertEqual(ldt.replace(tzinfo=None), udt + utcoffset)
4774 # Round trip
4775 self.assertEquivDatetimes(ldt.astimezone(timezone.utc),
4776 udt.replace(tzinfo=timezone.utc))
4777
4778
4779 for x in [-timedelta.resolution, shift]:
4780 udt = dt + x
4781 udt = udt.replace(tzinfo=tz)
4782 ldt = tz.fromutc(udt)
4783 self.assertEqual(ldt.fold, 0)
4784
4785 def test_gaps(self):
4786 tz = self.tz
4787 for dt, shift in tz.gaps():
4788 for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]:
4789 udt = dt + x
4790 udt = udt.replace(tzinfo=tz)
4791 ldt = tz.fromutc(udt)
4792 self.assertEqual(ldt.fold, 0)
4793 adt = udt.replace(tzinfo=timezone.utc).astimezone(tz)
4794 self.assertEquivDatetimes(adt, ldt)
4795 utcoffset = ldt.utcoffset()
4796 self.assertEqual(ldt.replace(tzinfo=None), udt.replace(tzinfo=No ne) + utcoffset)
4797 # Create a local time inside the gap
4798 ldt = tz.fromutc(dt.replace(tzinfo=tz)) - shift + x
4799 self.assertLess(ldt.replace(fold=1).utcoffset(),
4800 ldt.replace(fold=0).utcoffset(),
4801 "At %s." % ldt)
4802
4803 for x in [-timedelta.resolution, shift]:
4804 udt = dt + x
4805 ldt = tz.fromutc(udt.replace(tzinfo=tz))
4806 self.assertEqual(ldt.fold, 0)
4807
4808 def test_system_transitions(self):
4809 if ('Riyadh8' in self.zonename or
4810 # From tzdata NEWS file:
4811 # The files solar87, solar88, and solar89 are no longer distributed.
4812 # They were a negative experiment - that is, a demonstration that
4813 # tz data can represent solar time only with some difficulty and err or.
4814 # Their presence in the distribution caused confusion, as Riyadh
4815 # civil time was generally not solar time in those years.
4816 self.zonename.startswith('right/')):
4817 self.skipTest("Skipping %s" % self.zonename)
4818 tz = self.tz
4819 TZ = os.environ.get('TZ')
4820 os.environ['TZ'] = self.zonename
4821 try:
4822 _time.tzset()
4823 for udt, shift in tz.transitions():
4824 if self.zonename == 'Europe/Tallinn' and udt.date() == date(1999 , 10, 31):
4825 print("Skip %s %s transition" % (self.zonename, udt))
4826 continue
4827 if self.sizeof_time_t == 4 and udt.year >= 2037:
4828 print("Skip %s %s transition for 32-bit time_t" % (self.zone name, udt))
4829 continue
4830 s0 = (udt - datetime(1970, 1, 1)) // SEC
4831 ss = shift // SEC # shift seconds
4832 for x in [-40 * 3600, -20*3600, -1, 0,
4833 ss - 1, ss + 20 * 3600, ss + 40 * 3600]:
4834 s = s0 + x
4835 sdt = datetime.fromtimestamp(s)
4836 tzdt = datetime.fromtimestamp(s, tz).replace(tzinfo=None)
4837 self.assertEquivDatetimes(sdt, tzdt)
4838 s1 = sdt.timestamp()
4839 self.assertEqual(s, s1)
4840 if ss > 0: # gap
4841 # Create local time inside the gap
4842 dt = datetime.fromtimestamp(s0) - shift / 2
4843 ts0 = dt.timestamp()
4844 ts1 = dt.replace(fold=1).timestamp()
4845 self.assertEqual(ts0, s0 + ss / 2)
4846 self.assertEqual(ts1, s0 - ss / 2)
4847 finally:
4848 if TZ is None:
4849 del os.environ['TZ']
4850 else:
4851 os.environ['TZ'] = TZ
4852 _time.tzset()
4853
4854
4855 class ZoneInfoCompleteTest(unittest.TestSuite):
4856 def __init__(self):
4857 tests = []
4858 if is_resource_enabled('tzdata'):
4859 for name in ZoneInfo.zonenames():
4860 Test = type('ZoneInfoTest[%s]' % name, (ZoneInfoTest,), {})
4861 Test.zonename = name
4862 for method in dir(Test):
4863 if method.startswith('test_'):
4864 tests.append(Test(method))
4865 super().__init__(tests)
4866
4867 # Iran had a sub-minute UTC offset before 1946.
4868 class IranTest(ZoneInfoTest):
4869 zonename = 'Asia/Tehran'
4870
4871 def load_tests(loader, standard_tests, pattern):
4872 standard_tests.addTest(ZoneInfoCompleteTest())
4873 return standard_tests
4874
3815 4875
3816 if __name__ == "__main__": 4876 if __name__ == "__main__":
3817 test_main() 4877 unittest.main()
LEFTRIGHT

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+