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

Delta Between Two Patch Sets: Lib/datetime.py

Issue 15873: "datetime" cannot parse ISO 8601 dates and times
Left Patch Set: Created 7 years, 5 months ago
Right Patch Set: Created 3 years, 6 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 | « no previous file | Lib/test/datetimetester.py » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 """Concrete date/time and related types -- prototype implemented in Python. 1 """Concrete date/time and related types.
2 2
3 See http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage 3 See http://www.iana.org/time-zones/repository/tz-link.html for
4 4 time zone and DST data sources.
5 See also http://dir.yahoo.com/Reference/calendars/
6
7 For a primer on DST, including many current DST rules, see
8 http://webexhibits.org/daylightsaving/
9
10 For more about DST than you ever wanted to know, see
11 ftp://elsie.nci.nih.gov/pub/
12
13 Sources for time zone and DST data: http://www.twinsun.com/tz/tz-link.htm
14
15 This was originally copied from the sandbox of the CPython CVS repository.
16 Thanks to Tim Peters for suggesting using it.
17 """ 5 """
18 6
19 import time as _time 7 import time as _time
20 import math as _math 8 import math as _math
9 import re
21 10
22 def _cmp(x, y): 11 def _cmp(x, y):
23 return 0 if x == y else 1 if x > y else -1 12 return 0 if x == y else 1 if x > y else -1
24 13
25 MINYEAR = 1 14 MINYEAR = 1
26 MAXYEAR = 9999 15 MAXYEAR = 9999
27 _MAXORDINAL = 3652059 # date.max.toordinal() 16 _MAXORDINAL = 3652059 # date.max.toordinal()
28 17
29 # Utility functions, adapted from Python's Demo/classes/Dates.py, which 18 # Utility functions, adapted from Python's Demo/classes/Dates.py, which
30 # also assumes the current Gregorian calendar indefinitely extended in 19 # also assumes the current Gregorian calendar indefinitely extended in
31 # both directions. Difference: Dates.py calls January 1 of year 0 day 20 # both directions. Difference: Dates.py calls January 1 of year 0 day
32 # number 1. The code here calls January 1 of year 1 day number 1. This is 21 # number 1. The code here calls January 1 of year 1 day number 1. This is
33 # to match the definition of the "proleptic Gregorian" calendar in Dershowitz 22 # to match the definition of the "proleptic Gregorian" calendar in Dershowitz
34 # and Reingold's "Calendrical Calculations", where it's the base calendar 23 # and Reingold's "Calendrical Calculations", where it's the base calendar
35 # for all computations. See the book for algorithms for converting between 24 # for all computations. See the book for algorithms for converting between
36 # proleptic Gregorian ordinals and many other calendar systems. 25 # proleptic Gregorian ordinals and many other calendar systems.
37 26
38 _DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 27 # -1 is a placeholder for indexing purposes.
39 28 _DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
40 _DAYS_BEFORE_MONTH = [None] 29
30 _DAYS_BEFORE_MONTH = [-1] # -1 is a placeholder for indexing purposes.
41 dbm = 0 31 dbm = 0
42 for dim in _DAYS_IN_MONTH[1:]: 32 for dim in _DAYS_IN_MONTH[1:]:
43 _DAYS_BEFORE_MONTH.append(dbm) 33 _DAYS_BEFORE_MONTH.append(dbm)
44 dbm += dim 34 dbm += dim
45 del dbm, dim 35 del dbm, dim
46 36
47 def _is_leap(year): 37 def _is_leap(year):
48 "year -> 1 if leap year, else 0." 38 "year -> 1 if leap year, else 0."
49 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) 39 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
50 40
51 def _days_before_year(year): 41 def _days_before_year(year):
52 "year -> number of days before January 1st of year." 42 "year -> number of days before January 1st of year."
53 y = year - 1 43 y = year - 1
54 return y*365 + y//4 - y//100 + y//400 44 return y*365 + y//4 - y//100 + y//400
55 45
56 def _days_in_month(year, month): 46 def _days_in_month(year, month):
57 "year, month -> number of days in that month in that year." 47 "year, month -> number of days in that month in that year."
58 assert 1 <= month <= 12, month 48 assert 1 <= month <= 12, month
59 if month == 2 and _is_leap(year): 49 if month == 2 and _is_leap(year):
60 return 29 50 return 29
61 return _DAYS_IN_MONTH[month] 51 return _DAYS_IN_MONTH[month]
62 52
63 def _days_before_month(year, month): 53 def _days_before_month(year, month):
64 "year, month -> number of days in year preceeding first day of month." 54 "year, month -> number of days in year preceding first day of month."
65 assert 1 <= month <= 12, 'month must be in 1..12' 55 assert 1 <= month <= 12, 'month must be in 1..12'
66 return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) 56 return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
67 57
68 def _ymd2ord(year, month, day): 58 def _ymd2ord(year, month, day):
69 "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." 59 "year, month, day -> ordinal, considering 01-Jan-0001 as day 1."
70 assert 1 <= month <= 12, 'month must be in 1..12' 60 assert 1 <= month <= 12, 'month must be in 1..12'
71 dim = _days_in_month(year, month) 61 dim = _days_in_month(year, month)
72 assert 1 <= day <= dim, ('day must be in 1..%d' % dim) 62 assert 1 <= day <= dim, ('day must be in 1..%d' % dim)
73 return (_days_before_year(year) + 63 return (_days_before_year(year) +
74 _days_before_month(year, month) + 64 _days_before_month(year, month) +
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 _MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", 146 _MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
157 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 147 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
158 _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 148 _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
159 149
160 150
161 def _build_struct_time(y, m, d, hh, mm, ss, dstflag): 151 def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
162 wday = (_ymd2ord(y, m, d) + 6) % 7 152 wday = (_ymd2ord(y, m, d) + 6) % 7
163 dnum = _days_before_month(y, m) + d 153 dnum = _days_before_month(y, m) + d
164 return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) 154 return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
165 155
166 def _format_time(hh, mm, ss, us): 156 def _format_time(hh, mm, ss, us, timespec='auto'):
167 # Skip trailing microseconds when us==0. 157 specs = {
168 result = "%02d:%02d:%02d" % (hh, mm, ss) 158 'hours': '{:02d}',
169 if us: 159 'minutes': '{:02d}:{:02d}',
170 result += ".%06d" % us 160 'seconds': '{:02d}:{:02d}:{:02d}',
171 return result 161 'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}',
162 'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}'
163 }
164
165 if timespec == 'auto':
166 # Skip trailing microseconds when us==0.
167 timespec = 'microseconds' if us else 'seconds'
168 elif timespec == 'milliseconds':
169 us //= 1000
170 try:
171 fmt = specs[timespec]
172 except KeyError:
173 raise ValueError('Unknown timespec value')
174 else:
175 return fmt.format(hh, mm, ss, us)
172 176
173 # Correctly substitute for %z and %Z escapes in strftime formats. 177 # Correctly substitute for %z and %Z escapes in strftime formats.
174 def _wrap_strftime(object, format, timetuple): 178 def _wrap_strftime(object, format, timetuple):
175 # Don't call utcoffset() or tzname() unless actually needed. 179 # Don't call utcoffset() or tzname() unless actually needed.
176 freplace = None # the string to use for %f 180 freplace = None # the string to use for %f
177 zreplace = None # the string to use for %z 181 zreplace = None # the string to use for %z
178 Zreplace = None # the string to use for %Z 182 Zreplace = None # the string to use for %Z
179 183
180 # Scan format for %z and %Z escapes, replacing as needed. 184 # Scan format for %z and %Z escapes, replacing as needed.
181 newformat = [] 185 newformat = []
182 push = newformat.append 186 push = newformat.append
183 i, n = 0, len(format) 187 i, n = 0, len(format)
184 while i < n: 188 while i < n:
185 ch = format[i] 189 ch = format[i]
186 i += 1 190 i += 1
187 if ch == '%': 191 if ch == '%':
188 if i < n: 192 if i < n:
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 else: 225 else:
222 push('%') 226 push('%')
223 push(ch) 227 push(ch)
224 else: 228 else:
225 push('%') 229 push('%')
226 else: 230 else:
227 push(ch) 231 push(ch)
228 newformat = "".join(newformat) 232 newformat = "".join(newformat)
229 return _time.strftime(newformat, timetuple) 233 return _time.strftime(newformat, timetuple)
230 234
231 def _call_tzinfo_method(tzinfo, methname, tzinfoarg):
232 if tzinfo is None:
233 return None
234 return getattr(tzinfo, methname)(tzinfoarg)
235
236 # Just raise TypeError if the arg isn't None or a string. 235 # Just raise TypeError if the arg isn't None or a string.
237 def _check_tzname(name): 236 def _check_tzname(name):
238 if name is not None and not isinstance(name, str): 237 if name is not None and not isinstance(name, str):
239 raise TypeError("tzinfo.tzname() must return None or string, " 238 raise TypeError("tzinfo.tzname() must return None or string, "
240 "not '%s'" % type(name)) 239 "not '%s'" % type(name))
241 240
242 # name is the offset-producing method, "utcoffset" or "dst". 241 # name is the offset-producing method, "utcoffset" or "dst".
243 # offset is what it returned. 242 # offset is what it returned.
244 # If offset isn't None or timedelta, raises TypeError. 243 # If offset isn't None or timedelta, raises TypeError.
245 # If offset is None, returns None. 244 # If offset is None, returns None.
246 # Else offset is checked for being in range, and a whole # of minutes. 245 # Else offset is checked for being in range, and a whole # of minutes.
247 # If it is, its integer value is returned. Else ValueError is raised. 246 # If it is, its integer value is returned. Else ValueError is raised.
248 def _check_utc_offset(name, offset): 247 def _check_utc_offset(name, offset):
249 assert name in ("utcoffset", "dst") 248 assert name in ("utcoffset", "dst")
250 if offset is None: 249 if offset is None:
251 return 250 return
252 if not isinstance(offset, timedelta): 251 if not isinstance(offset, timedelta):
253 raise TypeError("tzinfo.%s() must return None " 252 raise TypeError("tzinfo.%s() must return None "
254 "or timedelta, not '%s'" % (name, type(offset))) 253 "or timedelta, not '%s'" % (name, type(offset)))
255 if offset % timedelta(minutes=1) or offset.microseconds: 254 if offset.microseconds:
256 raise ValueError("tzinfo.%s() must return a whole number " 255 raise ValueError("tzinfo.%s() must return a whole number "
257 "of minutes, got %s" % (name, offset)) 256 "of seconds, got %s" % (name, offset))
258 if not -timedelta(1) < offset < timedelta(1): 257 if not -timedelta(1) < offset < timedelta(1):
259 raise ValueError("%s()=%s, must be must be strictly between" 258 raise ValueError("%s()=%s, must be strictly between "
260 " -timedelta(hours=24) and timedelta(hours=24)" 259 "-timedelta(hours=24) and timedelta(hours=24)" %
261 % (name, offset)) 260 (name, offset))
261
262 def _check_int_field(value):
263 if isinstance(value, int):
264 return value
265 if not isinstance(value, float):
266 try:
267 value = value.__int__()
268 except AttributeError:
269 pass
270 else:
271 if isinstance(value, int):
272 return value
273 raise TypeError('__int__ returned non-int (type %s)' %
274 type(value).__name__)
275 raise TypeError('an integer is required (got type %s)' %
276 type(value).__name__)
277 raise TypeError('integer argument expected, got float')
262 278
263 def _check_date_fields(year, month, day): 279 def _check_date_fields(year, month, day):
264 if not isinstance(year, int): 280 year = _check_int_field(year)
265 raise TypeError('int expected') 281 month = _check_int_field(month)
282 day = _check_int_field(day)
266 if not MINYEAR <= year <= MAXYEAR: 283 if not MINYEAR <= year <= MAXYEAR:
267 raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year) 284 raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year)
268 if not 1 <= month <= 12: 285 if not 1 <= month <= 12:
269 raise ValueError('month must be in 1..12', month) 286 raise ValueError('month must be in 1..12', month)
270 dim = _days_in_month(year, month) 287 dim = _days_in_month(year, month)
271 if not 1 <= day <= dim: 288 if not 1 <= day <= dim:
272 raise ValueError('day must be in 1..%d' % dim, day) 289 raise ValueError('day must be in 1..%d' % dim, day)
290 return year, month, day
273 291
274 def _check_time_fields(hour, minute, second, microsecond): 292 def _check_time_fields(hour, minute, second, microsecond):
275 if not isinstance(hour, int): 293 hour = _check_int_field(hour)
276 raise TypeError('int expected') 294 minute = _check_int_field(minute)
295 second = _check_int_field(second)
296 microsecond = _check_int_field(microsecond)
277 if not 0 <= hour <= 23: 297 if not 0 <= hour <= 23:
278 raise ValueError('hour must be in 0..23', hour) 298 raise ValueError('hour must be in 0..23', hour)
279 if not 0 <= minute <= 59: 299 if not 0 <= minute <= 59:
280 raise ValueError('minute must be in 0..59', minute) 300 raise ValueError('minute must be in 0..59', minute)
281 if not 0 <= second <= 59: 301 if not 0 <= second <= 59:
282 raise ValueError('second must be in 0..59', second) 302 raise ValueError('second must be in 0..59', second)
283 if not 0 <= microsecond <= 999999: 303 if not 0 <= microsecond <= 999999:
284 raise ValueError('microsecond must be in 0..999999', microsecond) 304 raise ValueError('microsecond must be in 0..999999', microsecond)
305 return hour, minute, second, microsecond
285 306
286 def _check_tzinfo_arg(tz): 307 def _check_tzinfo_arg(tz):
287 if tz is not None and not isinstance(tz, tzinfo): 308 if tz is not None and not isinstance(tz, tzinfo):
288 raise TypeError("tzinfo argument must be None or of a tzinfo subclass") 309 raise TypeError("tzinfo argument must be None or of a tzinfo subclass")
289 310
290 def _cmperror(x, y): 311 def _cmperror(x, y):
291 raise TypeError("can't compare '%s' to '%s'" % ( 312 raise TypeError("can't compare '%s' to '%s'" % (
292 type(x).__name__, type(y).__name__)) 313 type(x).__name__, type(y).__name__))
293 314
315 def _divide_and_round(a, b):
316 """divide a by b and round result to the nearest integer
317
318 When the ratio is exactly half-way between two integers,
319 the even integer is returned.
320 """
321 # Based on the reference implementation for divmod_near
322 # in Objects/longobject.c.
323 q, r = divmod(a, b)
324 # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
325 # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
326 # positive, 2 * r < b if b negative.
327 r *= 2
328 greater_than_half = r > b if b > 0 else r < b
329 if greater_than_half or r == b and q % 2 == 1:
330 q += 1
331
332 return q
333
334
335 def _parse_isotime(cls, isostring):
336 match = cls._isore.match(isostring)
337 if not match:
338 raise ValueError("invalid RFC 3339 %s string: %r"
339 % (cls.__name__, isostring))
340 kw = match.groupdict()
341 tzinfo = kw.pop('tzinfo', None)
342 if tzinfo == 'Z' or tzinfo == 'z':
343 tzinfo = timezone.utc
344 elif tzinfo is not None:
345 offset_hours, _, offset_mins = tzinfo[1:].partition(':')
346 offset = timedelta(hours=int(offset_hours), minutes=int(offset_mins))
347 if tzinfo[0] == '-':
348 offset = -offset
349 tzinfo = timezone(offset)
350 us = kw.pop('microsecond', None)
351 kw = {k: int(v) for k, v in kw.items()}
352 if us:
353 us = round(float(us), 6)
354 kw['microsecond'] = int(us * 1e6)
355 if tzinfo:
356 kw['tzinfo'] = tzinfo
357 return cls(**kw)
358
359
294 class timedelta: 360 class timedelta:
295 """Represent the difference between two datetime objects. 361 """Represent the difference between two datetime objects.
296 362
297 Supported operators: 363 Supported operators:
298 364
299 - add, subtract timedelta 365 - add, subtract timedelta
300 - unary plus, minus, abs 366 - unary plus, minus, abs
301 - compare to timedelta 367 - compare to timedelta
302 - multiply, divide by int/long 368 - multiply, divide by int
303 369
304 In addition, datetime supports subtraction of two datetime objects 370 In addition, datetime supports subtraction of two datetime objects
305 returning a timedelta, and addition or subtraction of a datetime 371 returning a timedelta, and addition or subtraction of a datetime
306 and a timedelta giving a datetime. 372 and a timedelta giving a datetime.
307 373
308 Representation: (days, seconds, microseconds). Why? Because I 374 Representation: (days, seconds, microseconds). Why? Because I
309 felt like it. 375 felt like it.
310 """ 376 """
311 __slots__ = '_days', '_seconds', '_microseconds' 377 __slots__ = '_days', '_seconds', '_microseconds', '_hashcode'
312 378
313 def __new__(cls, days=0, seconds=0, microseconds=0, 379 def __new__(cls, days=0, seconds=0, microseconds=0,
314 milliseconds=0, minutes=0, hours=0, weeks=0): 380 milliseconds=0, minutes=0, hours=0, weeks=0):
315 # Doing this efficiently and accurately in C is going to be difficult 381 # Doing this efficiently and accurately in C is going to be difficult
316 # and error-prone, due to ubiquitous overflow possibilities, and that 382 # and error-prone, due to ubiquitous overflow possibilities, and that
317 # C double doesn't have enough bits of precision to represent 383 # C double doesn't have enough bits of precision to represent
318 # microseconds over 10K years faithfully. The code here tries to make 384 # microseconds over 10K years faithfully. The code here tries to make
319 # explicit where go-fast assumptions can be relied on, in order to 385 # explicit where go-fast assumptions can be relied on, in order to
320 # guide the C implementation; it's way more convoluted than speed- 386 # guide the C implementation; it's way more convoluted than speed-
321 # ignoring auto-overflow-to-long idiomatic Python could be. 387 # ignoring auto-overflow-to-long idiomatic Python could be.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
367 s += int(seconds) # can't overflow 433 s += int(seconds) # can't overflow
368 assert isinstance(s, int) 434 assert isinstance(s, int)
369 assert abs(s) <= 2 * 24 * 3600 435 assert abs(s) <= 2 * 24 * 3600
370 # seconds isn't referenced again before redefinition 436 # seconds isn't referenced again before redefinition
371 437
372 usdouble = secondsfrac * 1e6 438 usdouble = secondsfrac * 1e6
373 assert abs(usdouble) < 2.1e6 # exact value not critical 439 assert abs(usdouble) < 2.1e6 # exact value not critical
374 # secondsfrac isn't referenced again 440 # secondsfrac isn't referenced again
375 441
376 if isinstance(microseconds, float): 442 if isinstance(microseconds, float):
377 microseconds += usdouble 443 microseconds = round(microseconds + usdouble)
378 microseconds = round(microseconds, 0)
379 seconds, microseconds = divmod(microseconds, 1e6)
380 assert microseconds == int(microseconds)
381 assert seconds == int(seconds)
382 days, seconds = divmod(seconds, 24.*3600.)
383 assert days == int(days)
384 assert seconds == int(seconds)
385 d += int(days)
386 s += int(seconds) # can't overflow
387 assert isinstance(s, int)
388 assert abs(s) <= 3 * 24 * 3600
389 else:
390 seconds, microseconds = divmod(microseconds, 1000000) 444 seconds, microseconds = divmod(microseconds, 1000000)
391 days, seconds = divmod(seconds, 24*3600) 445 days, seconds = divmod(seconds, 24*3600)
392 d += days 446 d += days
393 s += int(seconds) # can't overflow 447 s += seconds
394 assert isinstance(s, int) 448 else:
395 assert abs(s) <= 3 * 24 * 3600 449 microseconds = int(microseconds)
396 microseconds = float(microseconds) 450 seconds, microseconds = divmod(microseconds, 1000000)
397 microseconds += usdouble 451 days, seconds = divmod(seconds, 24*3600)
398 microseconds = round(microseconds, 0) 452 d += days
453 s += seconds
454 microseconds = round(microseconds + usdouble)
455 assert isinstance(s, int)
456 assert isinstance(microseconds, int)
399 assert abs(s) <= 3 * 24 * 3600 457 assert abs(s) <= 3 * 24 * 3600
400 assert abs(microseconds) < 3.1e6 458 assert abs(microseconds) < 3.1e6
401 459
402 # Just a little bit of carrying possible for microseconds and seconds. 460 # Just a little bit of carrying possible for microseconds and seconds.
403 assert isinstance(microseconds, float) 461 seconds, us = divmod(microseconds, 1000000)
404 assert int(microseconds) == microseconds 462 s += seconds
405 us = int(microseconds)
406 seconds, us = divmod(us, 1000000)
407 s += seconds # cant't overflow
408 assert isinstance(s, int)
409 days, s = divmod(s, 24*3600) 463 days, s = divmod(s, 24*3600)
410 d += days 464 d += days
411 465
412 assert isinstance(d, int) 466 assert isinstance(d, int)
413 assert isinstance(s, int) and 0 <= s < 24*3600 467 assert isinstance(s, int) and 0 <= s < 24*3600
414 assert isinstance(us, int) and 0 <= us < 1000000 468 assert isinstance(us, int) and 0 <= us < 1000000
415 469
470 if abs(d) > 999999999:
471 raise OverflowError("timedelta # of days is too large: %d" % d)
472
416 self = object.__new__(cls) 473 self = object.__new__(cls)
417
418 self._days = d 474 self._days = d
419 self._seconds = s 475 self._seconds = s
420 self._microseconds = us 476 self._microseconds = us
421 if abs(d) > 999999999: 477 self._hashcode = -1
422 raise OverflowError("timedelta # of days is too large: %d" % d)
423
424 return self 478 return self
425 479
426 def __repr__(self): 480 def __repr__(self):
427 if self._microseconds: 481 if self._microseconds:
428 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, 482 return "%s.%s(%d, %d, %d)" % (self.__class__.__module__,
429 self._days, 483 self.__class__.__qualname__,
430 self._seconds, 484 self._days,
431 self._microseconds) 485 self._seconds,
486 self._microseconds)
432 if self._seconds: 487 if self._seconds:
433 return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__, 488 return "%s.%s(%d, %d)" % (self.__class__.__module__,
434 self._days, 489 self.__class__.__qualname__,
435 self._seconds) 490 self._days,
436 return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days) 491 self._seconds)
492 return "%s.%s(%d)" % (self.__class__.__module__,
493 self.__class__.__qualname__,
494 self._days)
437 495
438 def __str__(self): 496 def __str__(self):
439 mm, ss = divmod(self._seconds, 60) 497 mm, ss = divmod(self._seconds, 60)
440 hh, mm = divmod(mm, 60) 498 hh, mm = divmod(mm, 60)
441 s = "%d:%02d:%02d" % (hh, mm, ss) 499 s = "%d:%02d:%02d" % (hh, mm, ss)
442 if self._days: 500 if self._days:
443 def plural(n): 501 def plural(n):
444 return n, abs(n) != 1 and "s" or "" 502 return n, abs(n) != 1 and "s" or ""
445 s = ("%d day%s, " % plural(self._days)) + s 503 s = ("%d day%s, " % plural(self._days)) + s
446 if self._microseconds: 504 if self._microseconds:
447 s = s + ".%06d" % self._microseconds 505 s = s + ".%06d" % self._microseconds
448 return s 506 return s
449 507
450 def total_seconds(self): 508 def total_seconds(self):
451 """Total seconds in the duration.""" 509 """Total seconds in the duration."""
452 return ((self.days * 86400 + self.seconds)*10**6 + 510 return ((self.days * 86400 + self.seconds) * 10**6 +
453 self.microseconds) / 10**6 511 self.microseconds) / 10**6
454 512
455 # Read-only field accessors 513 # Read-only field accessors
456 @property 514 @property
457 def days(self): 515 def days(self):
458 """days""" 516 """days"""
459 return self._days 517 return self._days
460 518
461 @property 519 @property
462 def seconds(self): 520 def seconds(self):
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 return self 568 return self
511 569
512 def __mul__(self, other): 570 def __mul__(self, other):
513 if isinstance(other, int): 571 if isinstance(other, int):
514 # for CPython compatibility, we cannot use 572 # for CPython compatibility, we cannot use
515 # our __class__ here, but need a real timedelta 573 # our __class__ here, but need a real timedelta
516 return timedelta(self._days * other, 574 return timedelta(self._days * other,
517 self._seconds * other, 575 self._seconds * other,
518 self._microseconds * other) 576 self._microseconds * other)
519 if isinstance(other, float): 577 if isinstance(other, float):
578 usec = self._to_microseconds()
520 a, b = other.as_integer_ratio() 579 a, b = other.as_integer_ratio()
521 return self * a / b 580 return timedelta(0, 0, _divide_and_round(usec * a, b))
522 return NotImplemented 581 return NotImplemented
523 582
524 __rmul__ = __mul__ 583 __rmul__ = __mul__
525 584
526 def _to_microseconds(self): 585 def _to_microseconds(self):
527 return ((self._days * (24*3600) + self._seconds) * 1000000 + 586 return ((self._days * (24*3600) + self._seconds) * 1000000 +
528 self._microseconds) 587 self._microseconds)
529 588
530 def __floordiv__(self, other): 589 def __floordiv__(self, other):
531 if not isinstance(other, (int, timedelta)): 590 if not isinstance(other, (int, timedelta)):
532 return NotImplemented 591 return NotImplemented
533 usec = self._to_microseconds() 592 usec = self._to_microseconds()
534 if isinstance(other, timedelta): 593 if isinstance(other, timedelta):
535 return usec // other._to_microseconds() 594 return usec // other._to_microseconds()
536 if isinstance(other, int): 595 if isinstance(other, int):
537 return timedelta(0, 0, usec // other) 596 return timedelta(0, 0, usec // other)
538 597
539 def __truediv__(self, other): 598 def __truediv__(self, other):
540 if not isinstance(other, (int, float, timedelta)): 599 if not isinstance(other, (int, float, timedelta)):
541 return NotImplemented 600 return NotImplemented
542 usec = self._to_microseconds() 601 usec = self._to_microseconds()
543 if isinstance(other, timedelta): 602 if isinstance(other, timedelta):
544 return usec / other._to_microseconds() 603 return usec / other._to_microseconds()
545 if isinstance(other, int): 604 if isinstance(other, int):
546 return timedelta(0, 0, usec / other) 605 return timedelta(0, 0, _divide_and_round(usec, other))
547 if isinstance(other, float): 606 if isinstance(other, float):
548 a, b = other.as_integer_ratio() 607 a, b = other.as_integer_ratio()
549 return timedelta(0, 0, b * usec / a) 608 return timedelta(0, 0, _divide_and_round(b * usec, a))
550 609
551 def __mod__(self, other): 610 def __mod__(self, other):
552 if isinstance(other, timedelta): 611 if isinstance(other, timedelta):
553 r = self._to_microseconds() % other._to_microseconds() 612 r = self._to_microseconds() % other._to_microseconds()
554 return timedelta(0, 0, r) 613 return timedelta(0, 0, r)
555 return NotImplemented 614 return NotImplemented
556 615
557 def __divmod__(self, other): 616 def __divmod__(self, other):
558 if isinstance(other, timedelta): 617 if isinstance(other, timedelta):
559 q, r = divmod(self._to_microseconds(), 618 q, r = divmod(self._to_microseconds(),
560 other._to_microseconds()) 619 other._to_microseconds())
561 return q, timedelta(0, 0, r) 620 return q, timedelta(0, 0, r)
562 return NotImplemented 621 return NotImplemented
563 622
564 # Comparisons of timedelta objects with other. 623 # Comparisons of timedelta objects with other.
565 624
566 def __eq__(self, other): 625 def __eq__(self, other):
567 if isinstance(other, timedelta): 626 if isinstance(other, timedelta):
568 return self._cmp(other) == 0 627 return self._cmp(other) == 0
569 else: 628 else:
570 return False 629 return False
571 630
572 def __ne__(self, other):
573 if isinstance(other, timedelta):
574 return self._cmp(other) != 0
575 else:
576 return True
577
578 def __le__(self, other): 631 def __le__(self, other):
579 if isinstance(other, timedelta): 632 if isinstance(other, timedelta):
580 return self._cmp(other) <= 0 633 return self._cmp(other) <= 0
581 else: 634 else:
582 _cmperror(self, other) 635 _cmperror(self, other)
583 636
584 def __lt__(self, other): 637 def __lt__(self, other):
585 if isinstance(other, timedelta): 638 if isinstance(other, timedelta):
586 return self._cmp(other) < 0 639 return self._cmp(other) < 0
587 else: 640 else:
588 _cmperror(self, other) 641 _cmperror(self, other)
589 642
590 def __ge__(self, other): 643 def __ge__(self, other):
591 if isinstance(other, timedelta): 644 if isinstance(other, timedelta):
592 return self._cmp(other) >= 0 645 return self._cmp(other) >= 0
593 else: 646 else:
594 _cmperror(self, other) 647 _cmperror(self, other)
595 648
596 def __gt__(self, other): 649 def __gt__(self, other):
597 if isinstance(other, timedelta): 650 if isinstance(other, timedelta):
598 return self._cmp(other) > 0 651 return self._cmp(other) > 0
599 else: 652 else:
600 _cmperror(self, other) 653 _cmperror(self, other)
601 654
602 def _cmp(self, other): 655 def _cmp(self, other):
603 assert isinstance(other, timedelta) 656 assert isinstance(other, timedelta)
604 return _cmp(self._getstate(), other._getstate()) 657 return _cmp(self._getstate(), other._getstate())
605 658
606 def __hash__(self): 659 def __hash__(self):
607 return hash(self._getstate()) 660 if self._hashcode == -1:
661 self._hashcode = hash(self._getstate())
662 return self._hashcode
608 663
609 def __bool__(self): 664 def __bool__(self):
610 return (self._days != 0 or 665 return (self._days != 0 or
611 self._seconds != 0 or 666 self._seconds != 0 or
612 self._microseconds != 0) 667 self._microseconds != 0)
613 668
614 # Pickle support. 669 # Pickle support.
615 670
616 def _getstate(self): 671 def _getstate(self):
617 return (self._days, self._seconds, self._microseconds) 672 return (self._days, self._seconds, self._microseconds)
(...skipping 12 matching lines...) Expand all
630 Constructors: 685 Constructors:
631 686
632 __new__() 687 __new__()
633 fromtimestamp() 688 fromtimestamp()
634 today() 689 today()
635 fromordinal() 690 fromordinal()
636 691
637 Operators: 692 Operators:
638 693
639 __repr__, __str__ 694 __repr__, __str__
640 __cmp__, __hash__ 695 __eq__, __le__, __lt__, __ge__, __gt__, __hash__
641 __add__, __radd__, __sub__ (add/radd only with timedelta arg) 696 __add__, __radd__, __sub__ (add/radd only with timedelta arg)
642 697
643 Methods: 698 Methods:
644 699
645 timetuple() 700 timetuple()
646 toordinal() 701 toordinal()
647 weekday() 702 weekday()
648 isoweekday(), isocalendar(), isoformat() 703 isoweekday(), isocalendar(), isoformat()
649 ctime() 704 ctime()
650 strftime() 705 strftime()
651 706
652 Properties (readonly): 707 Properties (readonly):
653 year, month, day 708 year, month, day
654 """ 709 """
655 __slots__ = '_year', '_month', '_day' 710 __slots__ = '_year', '_month', '_day', '_hashcode'
711
712 _isore = re.compile(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})$', re. ASCII)
656 713
657 def __new__(cls, year, month=None, day=None): 714 def __new__(cls, year, month=None, day=None):
658 """Constructor. 715 """Constructor.
659 716
660 Arguments: 717 Arguments:
661 718
662 year, month, day (required, base 1) 719 year, month, day (required, base 1)
663 """ 720 """
664 if (isinstance(year, bytes) and len(year) == 4 and 721 if month is None and isinstance(year, bytes) and len(year) == 4 and \
665 1 <= year[2] <= 12 and month is None): # Month is sane 722 1 <= year[2] <= 12:
666 # Pickle support 723 # Pickle support
667 self = object.__new__(cls) 724 self = object.__new__(cls)
668 self.__setstate(year) 725 self.__setstate(year)
726 self._hashcode = -1
669 return self 727 return self
670 if isinstance(year, str) and month is None: 728 year, month, day = _check_date_fields(year, month, day)
671 if len(year) == 8:
672 y = year[:4]
673 m = year[4:6]
674 d = year[6:8]
675 elif len(year) == 10 and year[4] == year[7] == '-':
676 y,m,d = year.split('-')
677 else:
678 raise ValueError('invalid date string %r' % year)
679 try:
680 year = int(y)
681 month = int(m)
682 day = int(d)
683 except ValueError:
684 raise ValueError('invalid date string')
685 _check_date_fields(year, month, day)
686 self = object.__new__(cls) 729 self = object.__new__(cls)
687 self._year = year 730 self._year = year
688 self._month = month 731 self._month = month
689 self._day = day 732 self._day = day
733 self._hashcode = -1
690 return self 734 return self
691 735
692 # Additional constructors 736 # Additional constructors
693 737
694 @classmethod 738 @classmethod
695 def fromtimestamp(cls, t): 739 def fromtimestamp(cls, t):
696 "Construct a date from a POSIX timestamp (like time.time())." 740 "Construct a date from a POSIX timestamp (like time.time())."
697 y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t) 741 y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
698 return cls(y, m, d) 742 return cls(y, m, d)
699 743
700 @classmethod 744 @classmethod
701 def today(cls): 745 def today(cls):
702 "Construct a date from time.time()." 746 "Construct a date from time.time()."
703 t = _time.time() 747 t = _time.time()
704 return cls.fromtimestamp(t) 748 return cls.fromtimestamp(t)
705 749
706 @classmethod 750 @classmethod
707 def fromordinal(cls, n): 751 def fromordinal(cls, n):
708 """Contruct a date from a proleptic Gregorian ordinal. 752 """Construct a date from a proleptic Gregorian ordinal.
709 753
710 January 1 of year 1 is day 1. Only the year, month and day are 754 January 1 of year 1 is day 1. Only the year, month and day are
711 non-zero in the result. 755 non-zero in the result.
712 """ 756 """
713 y, m, d = _ord2ymd(n) 757 y, m, d = _ord2ymd(n)
714 return cls(y, m, d) 758 return cls(y, m, d)
715 759
760 @classmethod
761 def fromisoformat(cls, date_string):
762 """Constructs a date from an RFC 3339 string, a strict subset of ISO 860 1
763
764 Raises ValueError in case of ill-formatted or invalid string.
765 """
766 return _parse_isotime(cls, date_string)
767
716 # Conversions to string 768 # Conversions to string
717 769
718 def __repr__(self): 770 def __repr__(self):
719 """Convert to formal string, for repr(). 771 """Convert to formal string, for repr().
720 772
721 >>> dt = datetime(2010, 1, 1) 773 >>> dt = datetime(2010, 1, 1)
722 >>> repr(dt) 774 >>> repr(dt)
723 'datetime.datetime(2010, 1, 1, 0, 0)' 775 'datetime.datetime(2010, 1, 1, 0, 0)'
724 776
725 >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc) 777 >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc)
726 >>> repr(dt) 778 >>> repr(dt)
727 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)' 779 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)'
728 """ 780 """
729 return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, 781 return "%s.%s(%d, %d, %d)" % (self.__class__.__module__,
730 self._year, 782 self.__class__.__qualname__,
731 self._month, 783 self._year,
732 self._day) 784 self._month,
785 self._day)
733 # XXX These shouldn't depend on time.localtime(), because that 786 # XXX These shouldn't depend on time.localtime(), because that
734 # clips the usable dates to [1970 .. 2038). At least ctime() is 787 # clips the usable dates to [1970 .. 2038). At least ctime() is
735 # easily done without using strftime() -- that's better too because 788 # easily done without using strftime() -- that's better too because
736 # strftime("%c", ...) is locale specific. 789 # strftime("%c", ...) is locale specific.
737 790
738 791
739 def ctime(self): 792 def ctime(self):
740 "Return ctime() style string." 793 "Return ctime() style string."
741 weekday = self.toordinal() % 7 or 7 794 weekday = self.toordinal() % 7 or 7
742 return "%s %s %2d 00:00:00 %04d" % ( 795 return "%s %s %2d 00:00:00 %04d" % (
743 _DAYNAMES[weekday], 796 _DAYNAMES[weekday],
744 _MONTHNAMES[self._month], 797 _MONTHNAMES[self._month],
745 self._day, self._year) 798 self._day, self._year)
746 799
747 def strftime(self, fmt): 800 def strftime(self, fmt):
748 "Format using strftime()." 801 "Format using strftime()."
749 return _wrap_strftime(self, fmt, self.timetuple()) 802 return _wrap_strftime(self, fmt, self.timetuple())
750 803
751 def __format__(self, fmt): 804 def __format__(self, fmt):
805 if not isinstance(fmt, str):
806 raise TypeError("must be str, not %s" % type(fmt).__name__)
752 if len(fmt) != 0: 807 if len(fmt) != 0:
753 return self.strftime(fmt) 808 return self.strftime(fmt)
754 return str(self) 809 return str(self)
755 810
756 def isoformat(self): 811 def isoformat(self):
757 """Return the date formatted according to ISO. 812 """Return the date formatted according to ISO.
758 813
759 This is 'YYYY-MM-DD'. 814 This is 'YYYY-MM-DD'.
760 815
761 References: 816 References:
(...skipping 13 matching lines...) Expand all
775 @property 830 @property
776 def month(self): 831 def month(self):
777 """month (1-12)""" 832 """month (1-12)"""
778 return self._month 833 return self._month
779 834
780 @property 835 @property
781 def day(self): 836 def day(self):
782 """day (1-31)""" 837 """day (1-31)"""
783 return self._day 838 return self._day
784 839
785 # Standard conversions, __cmp__, __hash__ (and helpers) 840 # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,
841 # __hash__ (and helpers)
786 842
787 def timetuple(self): 843 def timetuple(self):
788 "Return local time tuple compatible with time.localtime()." 844 "Return local time tuple compatible with time.localtime()."
789 return _build_struct_time(self._year, self._month, self._day, 845 return _build_struct_time(self._year, self._month, self._day,
790 0, 0, 0, -1) 846 0, 0, 0, -1)
791 847
792 def toordinal(self): 848 def toordinal(self):
793 """Return proleptic Gregorian ordinal for the year, month and day. 849 """Return proleptic Gregorian ordinal for the year, month and day.
794 850
795 January 1 of year 1 is day 1. Only the year, month and day values 851 January 1 of year 1 is day 1. Only the year, month and day values
796 contribute to the result. 852 contribute to the result.
797 """ 853 """
798 return _ymd2ord(self._year, self._month, self._day) 854 return _ymd2ord(self._year, self._month, self._day)
799 855
800 def replace(self, year=None, month=None, day=None): 856 def replace(self, year=None, month=None, day=None):
801 """Return a new date with new values for the specified fields.""" 857 """Return a new date with new values for the specified fields."""
802 if year is None: 858 if year is None:
803 year = self._year 859 year = self._year
804 if month is None: 860 if month is None:
805 month = self._month 861 month = self._month
806 if day is None: 862 if day is None:
807 day = self._day 863 day = self._day
808 _check_date_fields(year, month, day)
809 return date(year, month, day) 864 return date(year, month, day)
810 865
811 # Comparisons of date objects with other. 866 # Comparisons of date objects with other.
812 867
813 def __eq__(self, other): 868 def __eq__(self, other):
814 if isinstance(other, date): 869 if isinstance(other, date):
815 return self._cmp(other) == 0 870 return self._cmp(other) == 0
816 return NotImplemented
817
818 def __ne__(self, other):
819 if isinstance(other, date):
820 return self._cmp(other) != 0
821 return NotImplemented 871 return NotImplemented
822 872
823 def __le__(self, other): 873 def __le__(self, other):
824 if isinstance(other, date): 874 if isinstance(other, date):
825 return self._cmp(other) <= 0 875 return self._cmp(other) <= 0
826 return NotImplemented 876 return NotImplemented
827 877
828 def __lt__(self, other): 878 def __lt__(self, other):
829 if isinstance(other, date): 879 if isinstance(other, date):
830 return self._cmp(other) < 0 880 return self._cmp(other) < 0
(...skipping 10 matching lines...) Expand all
841 return NotImplemented 891 return NotImplemented
842 892
843 def _cmp(self, other): 893 def _cmp(self, other):
844 assert isinstance(other, date) 894 assert isinstance(other, date)
845 y, m, d = self._year, self._month, self._day 895 y, m, d = self._year, self._month, self._day
846 y2, m2, d2 = other._year, other._month, other._day 896 y2, m2, d2 = other._year, other._month, other._day
847 return _cmp((y, m, d), (y2, m2, d2)) 897 return _cmp((y, m, d), (y2, m2, d2))
848 898
849 def __hash__(self): 899 def __hash__(self):
850 "Hash." 900 "Hash."
851 return hash(self._getstate()) 901 if self._hashcode == -1:
902 self._hashcode = hash(self._getstate())
903 return self._hashcode
852 904
853 # Computations 905 # Computations
854 906
855 def __add__(self, other): 907 def __add__(self, other):
856 "Add a date to a timedelta." 908 "Add a date to a timedelta."
857 if isinstance(other, timedelta): 909 if isinstance(other, timedelta):
858 o = self.toordinal() + other.days 910 o = self.toordinal() + other.days
859 if 0 < o <= _MAXORDINAL: 911 if 0 < o <= _MAXORDINAL:
860 return date.fromordinal(o) 912 return date.fromordinal(o)
861 raise OverflowError("result out of range") 913 raise OverflowError("result out of range")
(...skipping 26 matching lines...) Expand all
888 """Return a 3-tuple containing ISO year, week number, and weekday. 940 """Return a 3-tuple containing ISO year, week number, and weekday.
889 941
890 The first ISO week of the year is the (Mon-Sun) week 942 The first ISO week of the year is the (Mon-Sun) week
891 containing the year's first Thursday; everything else derives 943 containing the year's first Thursday; everything else derives
892 from that. 944 from that.
893 945
894 The first week is 1; Monday is 1 ... Sunday is 7. 946 The first week is 1; Monday is 1 ... Sunday is 7.
895 947
896 ISO calendar algorithm taken from 948 ISO calendar algorithm taken from
897 http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm 949 http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
950 (used with permission)
898 """ 951 """
899 year = self._year 952 year = self._year
900 week1monday = _isoweek1monday(year) 953 week1monday = _isoweek1monday(year)
901 today = _ymd2ord(self._year, self._month, self._day) 954 today = _ymd2ord(self._year, self._month, self._day)
902 # Internally, week and day have origin 0 955 # Internally, week and day have origin 0
903 week, day = divmod(today - week1monday, 7) 956 week, day = divmod(today - week1monday, 7)
904 if week < 0: 957 if week < 0:
905 year -= 1 958 year -= 1
906 week1monday = _isoweek1monday(year) 959 week1monday = _isoweek1monday(year)
907 week, day = divmod(today - week1monday, 7) 960 week, day = divmod(today - week1monday, 7)
908 elif week >= 52: 961 elif week >= 52:
909 if today >= _isoweek1monday(year+1): 962 if today >= _isoweek1monday(year+1):
910 year += 1 963 year += 1
911 week = 0 964 week = 0
912 return year, week+1, day+1 965 return year, week+1, day+1
913 966
914 # Pickle support. 967 # Pickle support.
915 968
916 def _getstate(self): 969 def _getstate(self, protocol=3):
917 yhi, ylo = divmod(self._year, 256) 970 yhi, ylo = divmod(self._year, 256)
918 return bytes([yhi, ylo, self._month, self._day]), 971 return bytes([yhi, ylo, self._month, self._day]),
919 972
920 def __setstate(self, string): 973 def __setstate(self, string):
921 if len(string) != 4 or not (1 <= string[2] <= 12):
922 raise TypeError("not enough arguments")
923 yhi, ylo, self._month, self._day = string 974 yhi, ylo, self._month, self._day = string
924 self._year = yhi * 256 + ylo 975 self._year = yhi * 256 + ylo
925 976
926 def __reduce__(self): 977 def __reduce_ex__(self, protocol):
927 return (self.__class__, self._getstate()) 978 return (self.__class__, self._getstate(protocol))
928 979
929 _date_class = date # so functions w/ args named "date" can get at the class 980 _date_class = date # so functions w/ args named "date" can get at the class
930 981
931 date.min = date(1, 1, 1) 982 date.min = date(1, 1, 1)
932 date.max = date(9999, 12, 31) 983 date.max = date(9999, 12, 31)
933 date.resolution = timedelta(days=1) 984 date.resolution = timedelta(days=1)
934 985
986
935 class tzinfo: 987 class tzinfo:
936 """Abstract base class for time zone info classes. 988 """Abstract base class for time zone info classes.
937 989
938 Subclasses must override the name(), utcoffset() and dst() methods. 990 Subclasses must override the name(), utcoffset() and dst() methods.
939 """ 991 """
940 __slots__ = () 992 __slots__ = ()
993
941 def tzname(self, dt): 994 def tzname(self, dt):
942 "datetime -> string name of time zone." 995 "datetime -> string name of time zone."
943 raise NotImplementedError("tzinfo subclass must override tzname()") 996 raise NotImplementedError("tzinfo subclass must override tzname()")
944 997
945 def utcoffset(self, dt): 998 def utcoffset(self, dt):
946 "datetime -> minutes east of UTC (negative for west of UTC)" 999 "datetime -> minutes east of UTC (negative for west of UTC)"
947 raise NotImplementedError("tzinfo subclass must override utcoffset()") 1000 raise NotImplementedError("tzinfo subclass must override utcoffset()")
948 1001
949 def dst(self, dt): 1002 def dst(self, dt):
950 """datetime -> DST offset in minutes east of UTC. 1003 """datetime -> DST offset in minutes east of UTC.
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
994 state = getstate() 1047 state = getstate()
995 else: 1048 else:
996 state = getattr(self, "__dict__", None) or None 1049 state = getattr(self, "__dict__", None) or None
997 if state is None: 1050 if state is None:
998 return (self.__class__, args) 1051 return (self.__class__, args)
999 else: 1052 else:
1000 return (self.__class__, args, state) 1053 return (self.__class__, args, state)
1001 1054
1002 _tzinfo_class = tzinfo 1055 _tzinfo_class = tzinfo
1003 1056
1057
1004 class time: 1058 class time:
1005 """Time with time zone. 1059 """Time with time zone.
1006 1060
1007 Constructors: 1061 Constructors:
1008 1062
1009 __new__() 1063 __new__()
1010 1064
1011 Operators: 1065 Operators:
1012 1066
1013 __repr__, __str__ 1067 __repr__, __str__
1014 __cmp__, __hash__ 1068 __eq__, __le__, __lt__, __ge__, __gt__, __hash__
1015 1069
1016 Methods: 1070 Methods:
1017 1071
1018 strftime() 1072 strftime()
1019 isoformat() 1073 isoformat()
1020 utcoffset() 1074 utcoffset()
1021 tzname() 1075 tzname()
1022 dst() 1076 dst()
1023 1077
1024 Properties (readonly): 1078 Properties (readonly):
1025 hour, minute, second, microsecond, tzinfo 1079 hour, minute, second, microsecond, tzinfo, fold
1026 """ 1080 """
1027 1081 __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hash code', '_fold'
1028 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None): 1082
1083 _isore = re.compile(r'(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})'
1084 r'(?P<microsecond>\.\d+)?(?P<tzinfo>Z|[+-]\d{2}:\d{2})?$ ',
1085 re.ASCII|re.IGNORECASE)
1086
1087
1088 def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):
1029 """Constructor. 1089 """Constructor.
1030 1090
1031 Arguments: 1091 Arguments:
1032 1092
1033 hour, minute (required) 1093 hour, minute (required)
1034 second, microsecond (default to zero) 1094 second, microsecond (default to zero)
1035 tzinfo (default to None) 1095 tzinfo (default to None)
1096 fold (keyword only, default to True)
1036 """ 1097 """
1098 if isinstance(hour, bytes) and len(hour) == 6 and hour[0]&0x7F < 24:
1099 # Pickle support
1100 self = object.__new__(cls)
1101 self.__setstate(hour, minute or None)
1102 self._hashcode = -1
1103 return self
1104 hour, minute, second, microsecond = _check_time_fields(
1105 hour, minute, second, microsecond)
1106 _check_tzinfo_arg(tzinfo)
1037 self = object.__new__(cls) 1107 self = object.__new__(cls)
1038 if isinstance(hour, bytes) and len(hour) == 6:
1039 # Pickle support
1040 self.__setstate(hour, minute or None)
1041 return self
1042 if isinstance(hour, str):
1043 if hour.endswith('Z'):
1044 hour = hour[:-1]
1045 tzinfo = timezone.utc
1046 idx = None
1047 if hour.count('-') == 1:
1048 idx = hour.index('-')
1049 if hour.count('+') == 1:
1050 idx = hour.index('+')
1051 if idx is not None:
1052 tzinfo = timezone(hour[idx:])
1053 hour = hour[:idx]
1054 idx = None
1055 if hour.count('.'):
1056 idx = hour.index('.')
1057 if hour.count(','):
1058 idx = hour.index(',')
1059 if idx is not None:
1060 microsecond = int(hour[idx+1:])
1061 hour = hour[:idx]
1062 if len(hour) == 2:
1063 hour = int(hour)
1064 elif len(hour) == 4:
1065 minute = int(hour[2:])
1066 hour = int(hour[:2])
1067 elif len(hour) == 5:
1068 if hour[2] == ':':
1069 minute = int(hour[3:])
1070 hour = int(hour[:2])
1071 else:
1072 raise ValueError('invalid time string')
1073 elif len(hour) == 6:
1074 second = int(hour[4:])
1075 minute = int(hour[2:4])
1076 hour = int(hour[:2])
1077 elif len(hour) == 8:
1078 if hour[2] == hour[5] == ':':
1079 second = int(hour[6:])
1080 minute = int(hour[3:5])
1081 hour = int(hour[:2])
1082 else:
1083 raise ValueError('invalid time string')
1084 _check_tzinfo_arg(tzinfo)
1085 _check_time_fields(hour, minute, second, microsecond)
1086 self._hour = hour 1108 self._hour = hour
1087 self._minute = minute 1109 self._minute = minute
1088 self._second = second 1110 self._second = second
1089 self._microsecond = microsecond 1111 self._microsecond = microsecond
1090 self._tzinfo = tzinfo 1112 self._tzinfo = tzinfo
1113 self._hashcode = -1
1114 self._fold = fold
1091 return self 1115 return self
1116
1117 @classmethod
1118 def fromisoformat(cls, time_string):
1119 """Constructs a time from an RFC 3339 string, a strict subset of ISO 860 1
1120
1121 Microseconds are rounded to 6 digits.
1122 Raises ValueError in case of ill-formatted or invalid string.
1123 """
1124 return _parse_isotime(cls, time_string)
1092 1125
1093 # Read-only field accessors 1126 # Read-only field accessors
1094 @property 1127 @property
1095 def hour(self): 1128 def hour(self):
1096 """hour (0-23)""" 1129 """hour (0-23)"""
1097 return self._hour 1130 return self._hour
1098 1131
1099 @property 1132 @property
1100 def minute(self): 1133 def minute(self):
1101 """minute (0-59)""" 1134 """minute (0-59)"""
1102 return self._minute 1135 return self._minute
1103 1136
1104 @property 1137 @property
1105 def second(self): 1138 def second(self):
1106 """second (0-59)""" 1139 """second (0-59)"""
1107 return self._second 1140 return self._second
1108 1141
1109 @property 1142 @property
1110 def microsecond(self): 1143 def microsecond(self):
1111 """microsecond (0-999999)""" 1144 """microsecond (0-999999)"""
1112 return self._microsecond 1145 return self._microsecond
1113 1146
1114 @property 1147 @property
1115 def tzinfo(self): 1148 def tzinfo(self):
1116 """timezone info object""" 1149 """timezone info object"""
1117 return self._tzinfo 1150 return self._tzinfo
1118 1151
1152 @property
1153 def fold(self):
1154 return self._fold
1155
1119 # Standard conversions, __hash__ (and helpers) 1156 # Standard conversions, __hash__ (and helpers)
1120 1157
1121 # Comparisons of time objects with other. 1158 # Comparisons of time objects with other.
1122 1159
1123 def __eq__(self, other): 1160 def __eq__(self, other):
1124 if isinstance(other, time): 1161 if isinstance(other, time):
1125 return self._cmp(other, allow_mixed=True) == 0 1162 return self._cmp(other, allow_mixed=True) == 0
1126 else: 1163 else:
1127 return False 1164 return False
1128
1129 def __ne__(self, other):
1130 if isinstance(other, time):
1131 return self._cmp(other, allow_mixed=True) != 0
1132 else:
1133 return True
1134 1165
1135 def __le__(self, other): 1166 def __le__(self, other):
1136 if isinstance(other, time): 1167 if isinstance(other, time):
1137 return self._cmp(other) <= 0 1168 return self._cmp(other) <= 0
1138 else: 1169 else:
1139 _cmperror(self, other) 1170 _cmperror(self, other)
1140 1171
1141 def __lt__(self, other): 1172 def __lt__(self, other):
1142 if isinstance(other, time): 1173 if isinstance(other, time):
1143 return self._cmp(other) < 0 1174 return self._cmp(other) < 0
(...skipping 21 matching lines...) Expand all
1165 if mytz is ottz: 1196 if mytz is ottz:
1166 base_compare = True 1197 base_compare = True
1167 else: 1198 else:
1168 myoff = self.utcoffset() 1199 myoff = self.utcoffset()
1169 otoff = other.utcoffset() 1200 otoff = other.utcoffset()
1170 base_compare = myoff == otoff 1201 base_compare = myoff == otoff
1171 1202
1172 if base_compare: 1203 if base_compare:
1173 return _cmp((self._hour, self._minute, self._second, 1204 return _cmp((self._hour, self._minute, self._second,
1174 self._microsecond), 1205 self._microsecond),
1175 (other._hour, other._minute, other._second, 1206 (other._hour, other._minute, other._second,
1176 other._microsecond)) 1207 other._microsecond))
1177 if myoff is None or otoff is None: 1208 if myoff is None or otoff is None:
1178 if allow_mixed: 1209 if allow_mixed:
1179 return 2 # arbitrary non-zero value 1210 return 2 # arbitrary non-zero value
1180 else: 1211 else:
1181 raise TypeError("cannot compare naive and aware times") 1212 raise TypeError("cannot compare naive and aware times")
1182 myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1) 1213 myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1)
1183 othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1) 1214 othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1)
1184 return _cmp((myhhmm, self._second, self._microsecond), 1215 return _cmp((myhhmm, self._second, self._microsecond),
1185 (othhmm, other._second, other._microsecond)) 1216 (othhmm, other._second, other._microsecond))
1186 1217
1187 def __hash__(self): 1218 def __hash__(self):
1188 """Hash.""" 1219 """Hash."""
1189 tzoff = self.utcoffset() 1220 if self._hashcode == -1:
1190 if not tzoff: # zero or None 1221 if self.fold:
1191 return hash(self._getstate()[0]) 1222 t = self.replace(fold=0)
1192 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff, 1223 else:
1193 timedelta(hours=1)) 1224 t = self
1194 assert not m % timedelta(minutes=1), "whole minute" 1225 tzoff = t.utcoffset()
1195 m //= timedelta(minutes=1) 1226 if not tzoff: # zero or None
1196 if 0 <= h < 24: 1227 self._hashcode = hash(t._getstate()[0])
1197 return hash(time(h, m, self.second, self.microsecond)) 1228 else:
1198 return hash((h, m, self.second, self.microsecond)) 1229 h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff,
1230 timedelta(hours=1))
1231 assert not m % timedelta(minutes=1), "whole minute"
1232 m //= timedelta(minutes=1)
1233 if 0 <= h < 24:
1234 self._hashcode = hash(time(h, m, self.second, self.microseco nd))
1235 else:
1236 self._hashcode = hash((h, m, self.second, self.microsecond))
1237 return self._hashcode
1199 1238
1200 # Conversion to string 1239 # Conversion to string
1201 1240
1202 def _tzstr(self, sep=":"): 1241 def _tzstr(self, sep=":"):
1203 """Return formatted timezone offset (+xx:xx) or None.""" 1242 """Return formatted timezone offset (+xx:xx) or None."""
1204 off = self.utcoffset() 1243 off = self.utcoffset()
1205 if off is not None: 1244 if off is not None:
1206 if off.days < 0: 1245 if off.days < 0:
1207 sign = "-" 1246 sign = "-"
1208 off = -off 1247 off = -off
1209 else: 1248 else:
1210 sign = "+" 1249 sign = "+"
1211 hh, mm = divmod(off, timedelta(hours=1)) 1250 hh, mm = divmod(off, timedelta(hours=1))
1212 assert not mm % timedelta(minutes=1), "whole minute" 1251 mm, ss = divmod(mm, timedelta(minutes=1))
1213 mm //= timedelta(minutes=1)
1214 assert 0 <= hh < 24 1252 assert 0 <= hh < 24
1215 off = "%s%02d%s%02d" % (sign, hh, sep, mm) 1253 off = "%s%02d%s%02d" % (sign, hh, sep, mm)
1254 if ss:
1255 off += ':%02d' % ss.seconds
1216 return off 1256 return off
1217 1257
1218 def __repr__(self): 1258 def __repr__(self):
1219 """Convert to formal string, for repr().""" 1259 """Convert to formal string, for repr()."""
1220 if self._microsecond != 0: 1260 if self._microsecond != 0:
1221 s = ", %d, %d" % (self._second, self._microsecond) 1261 s = ", %d, %d" % (self._second, self._microsecond)
1222 elif self._second != 0: 1262 elif self._second != 0:
1223 s = ", %d" % self._second 1263 s = ", %d" % self._second
1224 else: 1264 else:
1225 s = "" 1265 s = ""
1226 s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__, 1266 s= "%s.%s(%d, %d%s)" % (self.__class__.__module__,
1227 self._hour, self._minute, s) 1267 self.__class__.__qualname__,
1268 self._hour, self._minute, s)
1228 if self._tzinfo is not None: 1269 if self._tzinfo is not None:
1229 assert s[-1:] == ")" 1270 assert s[-1:] == ")"
1230 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" 1271 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1272 if self._fold:
1273 assert s[-1:] == ")"
1274 s = s[:-1] + ", fold=1)"
1231 return s 1275 return s
1232 1276
1233 def isoformat(self): 1277 def isoformat(self, timespec='auto'):
1234 """Return the time formatted according to ISO. 1278 """Return the time formatted according to ISO.
1235 1279
1236 This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if 1280 The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional
1237 self.microsecond == 0. 1281 part is omitted if self.microsecond == 0.
1282
1283 The optional argument timespec specifies the number of additional
1284 terms of the time to include.
1238 """ 1285 """
1239 s = _format_time(self._hour, self._minute, self._second, 1286 s = _format_time(self._hour, self._minute, self._second,
1240 self._microsecond) 1287 self._microsecond, timespec)
1241 tz = self._tzstr() 1288 tz = self._tzstr()
1242 if tz: 1289 if tz:
1243 s += tz 1290 s += tz
1244 return s 1291 return s
1245 1292
1246 __str__ = isoformat 1293 __str__ = isoformat
1247 1294
1248 def strftime(self, fmt): 1295 def strftime(self, fmt):
1249 """Format using strftime(). The date part of the timestamp passed 1296 """Format using strftime(). The date part of the timestamp passed
1250 to underlying strftime should not be used. 1297 to underlying strftime should not be used.
1251 """ 1298 """
1252 # The year must be >= 1000 else Python's strftime implementation 1299 # The year must be >= 1000 else Python's strftime implementation
1253 # can raise a bogus exception. 1300 # can raise a bogus exception.
1254 timetuple = (1900, 1, 1, 1301 timetuple = (1900, 1, 1,
1255 self._hour, self._minute, self._second, 1302 self._hour, self._minute, self._second,
1256 0, 1, -1) 1303 0, 1, -1)
1257 return _wrap_strftime(self, fmt, timetuple) 1304 return _wrap_strftime(self, fmt, timetuple)
1258 1305
1259 def __format__(self, fmt): 1306 def __format__(self, fmt):
1307 if not isinstance(fmt, str):
1308 raise TypeError("must be str, not %s" % type(fmt).__name__)
1260 if len(fmt) != 0: 1309 if len(fmt) != 0:
1261 return self.strftime(fmt) 1310 return self.strftime(fmt)
1262 return str(self) 1311 return str(self)
1263 1312
1264 # Timezone functions 1313 # Timezone functions
1265 1314
1266 def utcoffset(self): 1315 def utcoffset(self):
1267 """Return the timezone offset in minutes east of UTC (negative west of 1316 """Return the timezone offset in minutes east of UTC (negative west of
1268 UTC).""" 1317 UTC)."""
1269 if self._tzinfo is None: 1318 if self._tzinfo is None:
(...skipping 24 matching lines...) Expand all
1294 need to consult dst() unless you're interested in displaying the DST 1343 need to consult dst() unless you're interested in displaying the DST
1295 info. 1344 info.
1296 """ 1345 """
1297 if self._tzinfo is None: 1346 if self._tzinfo is None:
1298 return None 1347 return None
1299 offset = self._tzinfo.dst(None) 1348 offset = self._tzinfo.dst(None)
1300 _check_utc_offset("dst", offset) 1349 _check_utc_offset("dst", offset)
1301 return offset 1350 return offset
1302 1351
1303 def replace(self, hour=None, minute=None, second=None, microsecond=None, 1352 def replace(self, hour=None, minute=None, second=None, microsecond=None,
1304 tzinfo=True): 1353 tzinfo=True, *, fold=None):
1305 """Return a new time with new values for the specified fields.""" 1354 """Return a new time with new values for the specified fields."""
1306 if hour is None: 1355 if hour is None:
1307 hour = self.hour 1356 hour = self.hour
1308 if minute is None: 1357 if minute is None:
1309 minute = self.minute 1358 minute = self.minute
1310 if second is None: 1359 if second is None:
1311 second = self.second 1360 second = self.second
1312 if microsecond is None: 1361 if microsecond is None:
1313 microsecond = self.microsecond 1362 microsecond = self.microsecond
1314 if tzinfo is True: 1363 if tzinfo is True:
1315 tzinfo = self.tzinfo 1364 tzinfo = self.tzinfo
1316 _check_time_fields(hour, minute, second, microsecond) 1365 if fold is None:
1317 _check_tzinfo_arg(tzinfo) 1366 fold = self._fold
1318 return time(hour, minute, second, microsecond, tzinfo) 1367 return time(hour, minute, second, microsecond, tzinfo, fold=fold)
1319
1320 def __bool__(self):
1321 if self.second or self.microsecond:
1322 return True
1323 offset = self.utcoffset() or timedelta(0)
1324 return timedelta(hours=self.hour, minutes=self.minute) != offset
1325 1368
1326 # Pickle support. 1369 # Pickle support.
1327 1370
1328 def _getstate(self): 1371 def _getstate(self, protocol=3):
1329 us2, us3 = divmod(self._microsecond, 256) 1372 us2, us3 = divmod(self._microsecond, 256)
1330 us1, us2 = divmod(us2, 256) 1373 us1, us2 = divmod(us2, 256)
1331 basestate = bytes([self._hour, self._minute, self._second, 1374 h = self._hour
1375 if self._fold and protocol > 3:
1376 h += 128
1377 basestate = bytes([h, self._minute, self._second,
1332 us1, us2, us3]) 1378 us1, us2, us3])
1333 if self._tzinfo is None: 1379 if self._tzinfo is None:
1334 return (basestate,) 1380 return (basestate,)
1335 else: 1381 else:
1336 return (basestate, self._tzinfo) 1382 return (basestate, self._tzinfo)
1337 1383
1338 def __setstate(self, string, tzinfo): 1384 def __setstate(self, string, tzinfo):
1339 if len(string) != 6 or string[0] >= 24: 1385 if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
1340 raise TypeError("an integer is required") 1386 raise TypeError("bad tzinfo state arg")
1341 (self._hour, self._minute, self._second, 1387 h, self._minute, self._second, us1, us2, us3 = string
1342 us1, us2, us3) = string 1388 if h > 127:
1389 self._fold = 1
1390 self._hour = h - 128
1391 else:
1392 self._fold = 0
1393 self._hour = h
1343 self._microsecond = (((us1 << 8) | us2) << 8) | us3 1394 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1344 if tzinfo is None or isinstance(tzinfo, _tzinfo_class): 1395 self._tzinfo = tzinfo
1345 self._tzinfo = tzinfo 1396
1346 else: 1397 def __reduce_ex__(self, protocol):
1347 raise TypeError("bad tzinfo state arg %r" % tzinfo) 1398 return (time, self._getstate(protocol))
1348
1349 def __reduce__(self):
1350 return (time, self._getstate())
1351 1399
1352 _time_class = time # so functions w/ args named "time" can get at the class 1400 _time_class = time # so functions w/ args named "time" can get at the class
1353 1401
1354 time.min = time(0, 0, 0) 1402 time.min = time(0, 0, 0)
1355 time.max = time(23, 59, 59, 999999) 1403 time.max = time(23, 59, 59, 999999)
1356 time.resolution = timedelta(microseconds=1) 1404 time.resolution = timedelta(microseconds=1)
1357 1405
1358 class datetime(date): 1406 class datetime(date):
1359 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo] ]]]]) 1407 """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo] ]]]])
1360 1408
1361 The year, month and day arguments are required. tzinfo may be None, or an 1409 The year, month and day arguments are required. tzinfo may be None, or an
1362 instance of a tzinfo subclass. The remaining arguments may be ints or longs. 1410 instance of a tzinfo subclass. The remaining arguments may be ints.
1363 """ 1411 """
1364 1412 __slots__ = date.__slots__ + time.__slots__
1365 __slots__ = date.__slots__ + ( 1413
1366 '_hour', '_minute', '_second', 1414 _isore = re.compile(date._isore.pattern[:-1] + r'[T ]' +
1367 '_microsecond', '_tzinfo') 1415 time._isore.pattern, re.ASCII|re.IGNORECASE)
1416
1368 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, 1417 def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0,
1369 microsecond=0, tzinfo=None): 1418 microsecond=0, tzinfo=None, *, fold=0):
1370 if isinstance(year, bytes) and len(year) == 10: 1419 if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2]&0x7F <= 12:
1371 # Pickle support 1420 # Pickle support
1372 self = date.__new__(cls, year[:4]) 1421 self = object.__new__(cls)
1373 self.__setstate(year, month) 1422 self.__setstate(year, month)
1423 self._hashcode = -1
1374 return self 1424 return self
1375 if isinstance(year, str): 1425 year, month, day = _check_date_fields(year, month, day)
1376 idx = None 1426 hour, minute, second, microsecond = _check_time_fields(
1377 if year.count('T') == 1: 1427 hour, minute, second, microsecond)
1378 idx = year.index('T')
1379 if year.count(' ') == 1:
1380 idx = year.index(' ')
1381 if idx is None:
1382 return date.__new__(cls, year)
1383 return cls.combine(date(year[:idx]), time(year[idx+1:]))
1384 _check_tzinfo_arg(tzinfo) 1428 _check_tzinfo_arg(tzinfo)
1385 _check_time_fields(hour, minute, second, microsecond) 1429 self = object.__new__(cls)
1386 self = date.__new__(cls, year, month, day) 1430 self._year = year
1431 self._month = month
1432 self._day = day
1387 self._hour = hour 1433 self._hour = hour
1388 self._minute = minute 1434 self._minute = minute
1389 self._second = second 1435 self._second = second
1390 self._microsecond = microsecond 1436 self._microsecond = microsecond
1391 self._tzinfo = tzinfo 1437 self._tzinfo = tzinfo
1438 self._hashcode = -1
1439 self._fold = fold
1392 return self 1440 return self
1393 1441
1394 # Read-only field accessors 1442 # Read-only field accessors
1395 @property 1443 @property
1396 def hour(self): 1444 def hour(self):
1397 """hour (0-23)""" 1445 """hour (0-23)"""
1398 return self._hour 1446 return self._hour
1399 1447
1400 @property 1448 @property
1401 def minute(self): 1449 def minute(self):
1402 """minute (0-59)""" 1450 """minute (0-59)"""
1403 return self._minute 1451 return self._minute
1404 1452
1405 @property 1453 @property
1406 def second(self): 1454 def second(self):
1407 """second (0-59)""" 1455 """second (0-59)"""
1408 return self._second 1456 return self._second
1409 1457
1410 @property 1458 @property
1411 def microsecond(self): 1459 def microsecond(self):
1412 """microsecond (0-999999)""" 1460 """microsecond (0-999999)"""
1413 return self._microsecond 1461 return self._microsecond
1414 1462
1415 @property 1463 @property
1416 def tzinfo(self): 1464 def tzinfo(self):
1417 """timezone info object""" 1465 """timezone info object"""
1418 return self._tzinfo 1466 return self._tzinfo
1419 1467
1468 @property
1469 def fold(self):
1470 return self._fold
1471
1472 @classmethod
1473 def _fromtimestamp(cls, t, utc, tz):
1474 """Construct a datetime from a POSIX timestamp (like time.time()).
1475
1476 A timezone info object may be passed in as well.
1477 """
1478 frac, t = _math.modf(t)
1479 us = round(frac * 1e6)
1480 if us >= 1000000:
1481 t += 1
1482 us -= 1000000
1483 elif us < 0:
1484 t -= 1
1485 us += 1000000
1486
1487 converter = _time.gmtime if utc else _time.localtime
1488 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
1489 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1490 result = cls(y, m, d, hh, mm, ss, us, tz)
1491 if tz is None:
1492 # As of version 2015f max fold in IANA database is
1493 # 23 hours at 1969-09-30 13:00:00 in Kwajalein.
1494 # Let's probe 24 hours in the past to detect a transition:
1495 max_fold_seconds = 24 * 3600
1496 y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
1497 probe1 = cls(y, m, d, hh, mm, ss, us, tz)
1498 trans = result - probe1 - timedelta(0, max_fold_seconds)
1499 if trans.days < 0:
1500 y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6 ]
1501 probe2 = cls(y, m, d, hh, mm, ss, us, tz)
1502 if probe2 == result:
1503 result._fold = 1
1504 else:
1505 result = tz.fromutc(result)
1506 return result
1507
1420 @classmethod 1508 @classmethod
1421 def fromtimestamp(cls, t, tz=None): 1509 def fromtimestamp(cls, t, tz=None):
1422 """Construct a datetime from a POSIX timestamp (like time.time()). 1510 """Construct a datetime from a POSIX timestamp (like time.time()).
1423 1511
1424 A timezone info object may be passed in as well. 1512 A timezone info object may be passed in as well.
1425 """ 1513 """
1426
1427 _check_tzinfo_arg(tz) 1514 _check_tzinfo_arg(tz)
1428 1515
1429 converter = _time.localtime if tz is None else _time.gmtime 1516 return cls._fromtimestamp(t, tz is not None, tz)
1430
1431 t, frac = divmod(t, 1.0)
1432 us = int(frac * 1e6)
1433
1434 # If timestamp is less than one microsecond smaller than a
1435 # full second, us can be rounded up to 1000000. In this case,
1436 # roll over to seconds, otherwise, ValueError is raised
1437 # by the constructor.
1438 if us == 1000000:
1439 t += 1
1440 us = 0
1441 y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
1442 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1443 result = cls(y, m, d, hh, mm, ss, us, tz)
1444 if tz is not None:
1445 result = tz.fromutc(result)
1446 return result
1447 1517
1448 @classmethod 1518 @classmethod
1449 def utcfromtimestamp(cls, t): 1519 def utcfromtimestamp(cls, t):
1450 "Construct a UTC datetime from a POSIX timestamp (like time.time())." 1520 """Construct a naive UTC datetime from a POSIX timestamp."""
1451 t, frac = divmod(t, 1.0) 1521 return cls._fromtimestamp(t, True, None)
1452 us = int(frac * 1e6) 1522
1453
1454 # If timestamp is less than one microsecond smaller than a
1455 # full second, us can be rounded up to 1000000. In this case,
1456 # roll over to seconds, otherwise, ValueError is raised
1457 # by the constructor.
1458 if us == 1000000:
1459 t += 1
1460 us = 0
1461 y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t)
1462 ss = min(ss, 59) # clamp out leap seconds if the platform has them
1463 return cls(y, m, d, hh, mm, ss, us)
1464
1465 # XXX This is supposed to do better than we *can* do by using time.time(),
1466 # XXX if the platform supports a more accurate way. The C implementation
1467 # XXX uses gettimeofday on platforms that have it, but that isn't
1468 # XXX available from Python. So now() may return different results
1469 # XXX across the implementations.
1470 @classmethod 1523 @classmethod
1471 def now(cls, tz=None): 1524 def now(cls, tz=None):
1472 "Construct a datetime from time.time() and optional time zone info." 1525 "Construct a datetime from time.time() and optional time zone info."
1473 t = _time.time() 1526 t = _time.time()
1474 return cls.fromtimestamp(t, tz) 1527 return cls.fromtimestamp(t, tz)
1475 1528
1476 @classmethod 1529 @classmethod
1477 def utcnow(cls): 1530 def utcnow(cls):
1478 "Construct a UTC datetime from time.time()." 1531 "Construct a UTC datetime from time.time()."
1479 t = _time.time() 1532 t = _time.time()
1480 return cls.utcfromtimestamp(t) 1533 return cls.utcfromtimestamp(t)
1481 1534
1482 @classmethod 1535 @classmethod
1483 def combine(cls, date, time): 1536 def combine(cls, date, time, tzinfo=True):
1484 "Construct a datetime from a given date and a given time." 1537 "Construct a datetime from a given date and a given time."
1485 if not isinstance(date, _date_class): 1538 if not isinstance(date, _date_class):
1486 raise TypeError("date argument must be a date instance") 1539 raise TypeError("date argument must be a date instance")
1487 if not isinstance(time, _time_class): 1540 if not isinstance(time, _time_class):
1488 raise TypeError("time argument must be a time instance") 1541 raise TypeError("time argument must be a time instance")
1542 if tzinfo is True:
1543 tzinfo = time.tzinfo
1489 return cls(date.year, date.month, date.day, 1544 return cls(date.year, date.month, date.day,
1490 time.hour, time.minute, time.second, time.microsecond, 1545 time.hour, time.minute, time.second, time.microsecond,
1491 time.tzinfo) 1546 tzinfo, fold=time.fold)
1492 1547
1493 def timetuple(self): 1548 def timetuple(self):
1494 "Return local time tuple compatible with time.localtime()." 1549 "Return local time tuple compatible with time.localtime()."
1495 dst = self.dst() 1550 dst = self.dst()
1496 if dst is None: 1551 if dst is None:
1497 dst = -1 1552 dst = -1
1498 elif dst: 1553 elif dst:
1499 dst = 1 1554 dst = 1
1500 else: 1555 else:
1501 dst = 0 1556 dst = 0
1502 return _build_struct_time(self.year, self.month, self.day, 1557 return _build_struct_time(self.year, self.month, self.day,
1503 self.hour, self.minute, self.second, 1558 self.hour, self.minute, self.second,
1504 dst) 1559 dst)
1505 1560
1561 def _mktime(self):
1562 """Return integer POSIX timestamp."""
1563 epoch = datetime(1970, 1, 1)
1564 max_fold_seconds = 24 * 3600
1565 t = (self - epoch) // timedelta(0, 1)
1566 def local(u):
1567 y, m, d, hh, mm, ss = _time.localtime(u)[:6]
1568 return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)
1569
1570 # Our goal is to solve t = local(u) for u.
1571 a = local(t) - t
1572 u1 = t - a
1573 t1 = local(u1)
1574 if t1 == t:
1575 # We found one solution, but it may not be the one we need.
1576 # Look for an earlier solution (if `fold` is 0), or a
1577 # later one (if `fold` is 1).
1578 u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]
1579 b = local(u2) - u2
1580 if a == b:
1581 return u1
1582 else:
1583 b = t1 - u1
1584 assert a != b
1585 u2 = t - b
1586 t2 = local(u2)
1587 if t2 == t:
1588 return u2
1589 if t1 == t:
1590 return u1
1591 # We have found both offsets a and b, but neither t - a nor t - b is
1592 # a solution. This means t is in the gap.
1593 return (max, min)[self.fold](u1, u2)
1594
1595
1506 def timestamp(self): 1596 def timestamp(self):
1507 "Return POSIX timestamp as float" 1597 "Return POSIX timestamp as float"
1508 if self._tzinfo is None: 1598 if self._tzinfo is None:
1509 return _time.mktime((self.year, self.month, self.day, 1599 s = self._mktime()
1510 self.hour, self.minute, self.second, 1600 return s + self.microsecond / 1e6
1511 -1, -1, -1)) + self.microsecond / 1e6
1512 else: 1601 else:
1513 return (self - _EPOCH).total_seconds() 1602 return (self - _EPOCH).total_seconds()
1514 1603
1515 def utctimetuple(self): 1604 def utctimetuple(self):
1516 "Return UTC time tuple compatible with time.gmtime()." 1605 "Return UTC time tuple compatible with time.gmtime()."
1517 offset = self.utcoffset() 1606 offset = self.utcoffset()
1518 if offset: 1607 if offset:
1519 self -= offset 1608 self -= offset
1520 y, m, d = self.year, self.month, self.day 1609 y, m, d = self.year, self.month, self.day
1521 hh, mm, ss = self.hour, self.minute, self.second 1610 hh, mm, ss = self.hour, self.minute, self.second
1522 return _build_struct_time(y, m, d, hh, mm, ss, 0) 1611 return _build_struct_time(y, m, d, hh, mm, ss, 0)
1523 1612
1524 def date(self): 1613 def date(self):
1525 "Return the date part." 1614 "Return the date part."
1526 return date(self._year, self._month, self._day) 1615 return date(self._year, self._month, self._day)
1527 1616
1528 def time(self): 1617 def time(self):
1529 "Return the time part, with tzinfo None." 1618 "Return the time part, with tzinfo None."
1530 return time(self.hour, self.minute, self.second, self.microsecond) 1619 return time(self.hour, self.minute, self.second, self.microsecond, fold= self.fold)
1531 1620
1532 def timetz(self): 1621 def timetz(self):
1533 "Return the time part, with same tzinfo." 1622 "Return the time part, with same tzinfo."
1534 return time(self.hour, self.minute, self.second, self.microsecond, 1623 return time(self.hour, self.minute, self.second, self.microsecond,
1535 self._tzinfo) 1624 self._tzinfo, fold=self.fold)
1536 1625
1537 def replace(self, year=None, month=None, day=None, hour=None, 1626 def replace(self, year=None, month=None, day=None, hour=None,
1538 minute=None, second=None, microsecond=None, tzinfo=True): 1627 minute=None, second=None, microsecond=None, tzinfo=True,
1628 *, fold=None):
1539 """Return a new datetime with new values for the specified fields.""" 1629 """Return a new datetime with new values for the specified fields."""
1540 if year is None: 1630 if year is None:
1541 year = self.year 1631 year = self.year
1542 if month is None: 1632 if month is None:
1543 month = self.month 1633 month = self.month
1544 if day is None: 1634 if day is None:
1545 day = self.day 1635 day = self.day
1546 if hour is None: 1636 if hour is None:
1547 hour = self.hour 1637 hour = self.hour
1548 if minute is None: 1638 if minute is None:
1549 minute = self.minute 1639 minute = self.minute
1550 if second is None: 1640 if second is None:
1551 second = self.second 1641 second = self.second
1552 if microsecond is None: 1642 if microsecond is None:
1553 microsecond = self.microsecond 1643 microsecond = self.microsecond
1554 if tzinfo is True: 1644 if tzinfo is True:
1555 tzinfo = self.tzinfo 1645 tzinfo = self.tzinfo
1556 _check_date_fields(year, month, day) 1646 if fold is None:
1557 _check_time_fields(hour, minute, second, microsecond) 1647 fold = self.fold
1558 _check_tzinfo_arg(tzinfo)
1559 return datetime(year, month, day, hour, minute, second, 1648 return datetime(year, month, day, hour, minute, second,
1560 microsecond, tzinfo) 1649 microsecond, tzinfo, fold=fold)
1650
1651 def _local_timezone(self):
1652 if self.tzinfo is None:
1653 ts = self._mktime()
1654 else:
1655 ts = (self - _EPOCH) // timedelta(seconds=1)
1656 localtm = _time.localtime(ts)
1657 local = datetime(*localtm[:6])
1658 try:
1659 # Extract TZ data if available
1660 gmtoff = localtm.tm_gmtoff
1661 zone = localtm.tm_zone
1662 except AttributeError:
1663 delta = local - datetime(*_time.gmtime(ts)[:6])
1664 zone = _time.strftime('%Z', localtm)
1665 tz = timezone(delta, zone)
1666 else:
1667 tz = timezone(timedelta(seconds=gmtoff), zone)
1668 return tz
1561 1669
1562 def astimezone(self, tz=None): 1670 def astimezone(self, tz=None):
1563 if tz is None: 1671 if tz is None:
1564 if self.tzinfo is None: 1672 tz = self._local_timezone()
1565 raise ValueError("astimezone() requires an aware datetime")
1566 ts = (self - _EPOCH) // timedelta(seconds=1)
1567 localtm = _time.localtime(ts)
1568 local = datetime(*localtm[:6])
1569 try:
1570 # Extract TZ data if available
1571 gmtoff = localtm.tm_gmtoff
1572 zone = localtm.tm_zone
1573 except AttributeError:
1574 # Compute UTC offset and compare with the value implied
1575 # by tm_isdst. If the values match, use the zone name
1576 # implied by tm_isdst.
1577 delta = local - datetime(*_time.gmtime(ts)[:6])
1578 dst = _time.daylight and localtm.tm_isdst > 0
1579 gmtoff = -(_time.altzone if dst else _time.timezone)
1580 if delta == timedelta(seconds=gmtoff):
1581 tz = timezone(delta, _time.tzname[dst])
1582 else:
1583 tz = timezone(delta)
1584 else:
1585 tz = timezone(timedelta(seconds=gmtoff), zone)
1586
1587 elif not isinstance(tz, tzinfo): 1673 elif not isinstance(tz, tzinfo):
1588 raise TypeError("tz argument must be an instance of tzinfo") 1674 raise TypeError("tz argument must be an instance of tzinfo")
1589 1675
1590 mytz = self.tzinfo 1676 mytz = self.tzinfo
1591 if mytz is None: 1677 if mytz is None:
1592 raise ValueError("astimezone() requires an aware datetime") 1678 mytz = self._local_timezone()
1593 1679
1594 if tz is mytz: 1680 if tz is mytz:
1595 return self 1681 return self
1596 1682
1597 # Convert self to UTC, and attach the new time zone object. 1683 # Convert self to UTC, and attach the new time zone object.
1598 myoffset = self.utcoffset() 1684 myoffset = mytz.utcoffset(self)
1599 if myoffset is None: 1685 if myoffset is None:
1600 raise ValueError("astimezone() requires an aware datetime") 1686 raise ValueError("astimezone() requires an aware datetime")
1601 utc = (self - myoffset).replace(tzinfo=tz) 1687 utc = (self - myoffset).replace(tzinfo=tz)
1602 1688
1603 # Convert from UTC to tz's local time. 1689 # Convert from UTC to tz's local time.
1604 return tz.fromutc(utc) 1690 return tz.fromutc(utc)
1605 1691
1606 # Ways to produce a string. 1692 # Ways to produce a string.
1607 1693
1608 def ctime(self): 1694 def ctime(self):
1609 "Return ctime() style string." 1695 "Return ctime() style string."
1610 weekday = self.toordinal() % 7 or 7 1696 weekday = self.toordinal() % 7 or 7
1611 return "%s %s %2d %02d:%02d:%02d %04d" % ( 1697 return "%s %s %2d %02d:%02d:%02d %04d" % (
1612 _DAYNAMES[weekday], 1698 _DAYNAMES[weekday],
1613 _MONTHNAMES[self._month], 1699 _MONTHNAMES[self._month],
1614 self._day, 1700 self._day,
1615 self._hour, self._minute, self._second, 1701 self._hour, self._minute, self._second,
1616 self._year) 1702 self._year)
1617 1703
1618 def isoformat(self, sep='T'): 1704 def isoformat(self, sep='T', timespec='auto'):
1619 """Return the time formatted according to ISO. 1705 """Return the time formatted according to ISO.
1620 1706
1621 This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if 1707 The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.
1622 self.microsecond == 0. 1708 By default, the fractional part is omitted if self.microsecond == 0.
1623 1709
1624 If self.tzinfo is not None, the UTC offset is also attached, giving 1710 If self.tzinfo is not None, the UTC offset is also attached, giving
1625 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'. 1711 giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.
1626 1712
1627 Optional argument sep specifies the separator between date and 1713 Optional argument sep specifies the separator between date and
1628 time, default 'T'. 1714 time, default 'T'.
1715
1716 The optional argument timespec specifies the number of additional
1717 terms of the time to include.
1629 """ 1718 """
1630 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, 1719 s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) +
1631 sep) + 1720 _format_time(self._hour, self._minute, self._second,
1632 _format_time(self._hour, self._minute, self._second, 1721 self._microsecond, timespec))
1633 self._microsecond)) 1722
1634 off = self.utcoffset() 1723 off = self.utcoffset()
1635 if off is not None: 1724 if off is not None:
1636 if off.days < 0: 1725 if off.days < 0:
1637 sign = "-" 1726 sign = "-"
1638 off = -off 1727 off = -off
1639 else: 1728 else:
1640 sign = "+" 1729 sign = "+"
1641 hh, mm = divmod(off, timedelta(hours=1)) 1730 hh, mm = divmod(off, timedelta(hours=1))
1642 assert not mm % timedelta(minutes=1), "whole minute" 1731 mm, ss = divmod(mm, timedelta(minutes=1))
1643 mm //= timedelta(minutes=1)
1644 s += "%s%02d:%02d" % (sign, hh, mm) 1732 s += "%s%02d:%02d" % (sign, hh, mm)
1733 if ss:
1734 assert not ss.microseconds
1735 s += ":%02d" % ss.seconds
1645 return s 1736 return s
1646 1737
1647 def __repr__(self): 1738 def __repr__(self):
1648 """Convert to formal string, for repr().""" 1739 """Convert to formal string, for repr()."""
1649 L = [self._year, self._month, self._day, # These are never zero 1740 L = [self._year, self._month, self._day, # These are never zero
1650 self._hour, self._minute, self._second, self._microsecond] 1741 self._hour, self._minute, self._second, self._microsecond]
1651 if L[-1] == 0: 1742 if L[-1] == 0:
1652 del L[-1] 1743 del L[-1]
1653 if L[-1] == 0: 1744 if L[-1] == 0:
1654 del L[-1] 1745 del L[-1]
1655 s = ", ".join(map(str, L)) 1746 s = "%s.%s(%s)" % (self.__class__.__module__,
1656 s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s) 1747 self.__class__.__qualname__,
1748 ", ".join(map(str, L)))
1657 if self._tzinfo is not None: 1749 if self._tzinfo is not None:
1658 assert s[-1:] == ")" 1750 assert s[-1:] == ")"
1659 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" 1751 s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1752 if self._fold:
1753 assert s[-1:] == ")"
1754 s = s[:-1] + ", fold=1)"
1660 return s 1755 return s
1661 1756
1662 def __str__(self): 1757 def __str__(self):
1663 "Convert to string, for str()." 1758 "Convert to string, for str()."
1664 return self.isoformat(sep=' ') 1759 return self.isoformat(sep=' ')
1665 1760
1666 @classmethod 1761 @classmethod
1667 def strptime(cls, date_string, format): 1762 def strptime(cls, date_string, format):
1668 'string, format -> new datetime parsed from a string (like time.strptime ()).' 1763 'string, format -> new datetime parsed from a string (like time.strptime ()).'
1669 import _strptime 1764 import _strptime
1670 return _strptime._strptime_datetime(cls, date_string, format) 1765 return _strptime._strptime_datetime(cls, date_string, format)
1671 1766
1672 def utcoffset(self): 1767 def utcoffset(self):
1673 """Return the timezone offset in minutes east of UTC (negative west of 1768 """Return the timezone offset in minutes east of UTC (negative west of
1674 UTC).""" 1769 UTC)."""
1675 if self._tzinfo is None: 1770 if self._tzinfo is None:
1676 return None 1771 return None
1677 offset = self._tzinfo.utcoffset(self) 1772 offset = self._tzinfo.utcoffset(self)
1678 _check_utc_offset("utcoffset", offset) 1773 _check_utc_offset("utcoffset", offset)
1679 return offset 1774 return offset
1680 1775
1681 def tzname(self): 1776 def tzname(self):
1682 """Return the timezone name. 1777 """Return the timezone name.
1683 1778
1684 Note that the name is 100% informational -- there's no requirement that 1779 Note that the name is 100% informational -- there's no requirement that
1685 it mean anything in particular. For example, "GMT", "UTC", "-500", 1780 it mean anything in particular. For example, "GMT", "UTC", "-500",
1686 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. 1781 "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1687 """ 1782 """
1688 name = _call_tzinfo_method(self._tzinfo, "tzname", self) 1783 if self._tzinfo is None:
1784 return None
1785 name = self._tzinfo.tzname(self)
1689 _check_tzname(name) 1786 _check_tzname(name)
1690 return name 1787 return name
1691 1788
1692 def dst(self): 1789 def dst(self):
1693 """Return 0 if DST is not in effect, or the DST offset (in minutes 1790 """Return 0 if DST is not in effect, or the DST offset (in minutes
1694 eastward) if DST is in effect. 1791 eastward) if DST is in effect.
1695 1792
1696 This is purely informational; the DST offset has already been added to 1793 This is purely informational; the DST offset has already been added to
1697 the UTC offset returned by utcoffset() if applicable, so there's no 1794 the UTC offset returned by utcoffset() if applicable, so there's no
1698 need to consult dst() unless you're interested in displaying the DST 1795 need to consult dst() unless you're interested in displaying the DST
1699 info. 1796 info.
1700 """ 1797 """
1701 if self._tzinfo is None: 1798 if self._tzinfo is None:
1702 return None 1799 return None
1703 offset = self._tzinfo.dst(self) 1800 offset = self._tzinfo.dst(self)
1704 _check_utc_offset("dst", offset) 1801 _check_utc_offset("dst", offset)
1705 return offset 1802 return offset
1706 1803
1707 # Comparisons of datetime objects with other. 1804 # Comparisons of datetime objects with other.
1708 1805
1709 def __eq__(self, other): 1806 def __eq__(self, other):
1710 if isinstance(other, datetime): 1807 if isinstance(other, datetime):
1711 return self._cmp(other, allow_mixed=True) == 0 1808 return self._cmp(other, allow_mixed=True) == 0
1712 elif not isinstance(other, date): 1809 elif not isinstance(other, date):
1713 return NotImplemented 1810 return NotImplemented
1714 else: 1811 else:
1715 return False 1812 return False
1716 1813
1717 def __ne__(self, other):
1718 if isinstance(other, datetime):
1719 return self._cmp(other, allow_mixed=True) != 0
1720 elif not isinstance(other, date):
1721 return NotImplemented
1722 else:
1723 return True
1724
1725 def __le__(self, other): 1814 def __le__(self, other):
1726 if isinstance(other, datetime): 1815 if isinstance(other, datetime):
1727 return self._cmp(other) <= 0 1816 return self._cmp(other) <= 0
1728 elif not isinstance(other, date): 1817 elif not isinstance(other, date):
1729 return NotImplemented 1818 return NotImplemented
1730 else: 1819 else:
1731 _cmperror(self, other) 1820 _cmperror(self, other)
1732 1821
1733 def __lt__(self, other): 1822 def __lt__(self, other):
1734 if isinstance(other, datetime): 1823 if isinstance(other, datetime):
(...skipping 23 matching lines...) Expand all
1758 assert isinstance(other, datetime) 1847 assert isinstance(other, datetime)
1759 mytz = self._tzinfo 1848 mytz = self._tzinfo
1760 ottz = other._tzinfo 1849 ottz = other._tzinfo
1761 myoff = otoff = None 1850 myoff = otoff = None
1762 1851
1763 if mytz is ottz: 1852 if mytz is ottz:
1764 base_compare = True 1853 base_compare = True
1765 else: 1854 else:
1766 myoff = self.utcoffset() 1855 myoff = self.utcoffset()
1767 otoff = other.utcoffset() 1856 otoff = other.utcoffset()
1857 # Assume that allow_mixed means that we are called from __eq__
1858 if allow_mixed:
1859 if myoff != self.replace(fold=not self.fold).utcoffset():
1860 return 2
1861 if otoff != other.replace(fold=not other.fold).utcoffset():
1862 return 2
1768 base_compare = myoff == otoff 1863 base_compare = myoff == otoff
1769 1864
1770 if base_compare: 1865 if base_compare:
1771 return _cmp((self._year, self._month, self._day, 1866 return _cmp((self._year, self._month, self._day,
1772 self._hour, self._minute, self._second, 1867 self._hour, self._minute, self._second,
1773 self._microsecond), 1868 self._microsecond),
1774 (other._year, other._month, other._day, 1869 (other._year, other._month, other._day,
1775 other._hour, other._minute, other._second, 1870 other._hour, other._minute, other._second,
1776 other._microsecond)) 1871 other._microsecond))
1777 if myoff is None or otoff is None: 1872 if myoff is None or otoff is None:
1778 if allow_mixed: 1873 if allow_mixed:
1779 return 2 # arbitrary non-zero value 1874 return 2 # arbitrary non-zero value
1780 else: 1875 else:
1781 raise TypeError("cannot compare naive and aware datetimes") 1876 raise TypeError("cannot compare naive and aware datetimes")
1782 # XXX What follows could be done more efficiently... 1877 # XXX What follows could be done more efficiently...
1783 diff = self - other # this will take offsets into account 1878 diff = self - other # this will take offsets into account
1784 if diff.days < 0: 1879 if diff.days < 0:
1785 return -1 1880 return -1
1786 return diff and 1 or 0 1881 return diff and 1 or 0
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1824 return base 1919 return base
1825 myoff = self.utcoffset() 1920 myoff = self.utcoffset()
1826 otoff = other.utcoffset() 1921 otoff = other.utcoffset()
1827 if myoff == otoff: 1922 if myoff == otoff:
1828 return base 1923 return base
1829 if myoff is None or otoff is None: 1924 if myoff is None or otoff is None:
1830 raise TypeError("cannot mix naive and timezone-aware time") 1925 raise TypeError("cannot mix naive and timezone-aware time")
1831 return base + otoff - myoff 1926 return base + otoff - myoff
1832 1927
1833 def __hash__(self): 1928 def __hash__(self):
1834 tzoff = self.utcoffset() 1929 if self._hashcode == -1:
1835 if tzoff is None: 1930 if self.fold:
1836 return hash(self._getstate()[0]) 1931 t = self.replace(fold=0)
1837 days = _ymd2ord(self.year, self.month, self.day) 1932 else:
1838 seconds = self.hour * 3600 + self.minute * 60 + self.second 1933 t = self
1839 return hash(timedelta(days, seconds, self.microsecond) - tzoff) 1934 tzoff = t.utcoffset()
1935 if tzoff is None:
1936 self._hashcode = hash(t._getstate()[0])
1937 else:
1938 days = _ymd2ord(self.year, self.month, self.day)
1939 seconds = self.hour * 3600 + self.minute * 60 + self.second
1940 self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff)
1941 return self._hashcode
1840 1942
1841 # Pickle support. 1943 # Pickle support.
1842 1944
1843 def _getstate(self): 1945 def _getstate(self, protocol=3):
1844 yhi, ylo = divmod(self._year, 256) 1946 yhi, ylo = divmod(self._year, 256)
1845 us2, us3 = divmod(self._microsecond, 256) 1947 us2, us3 = divmod(self._microsecond, 256)
1846 us1, us2 = divmod(us2, 256) 1948 us1, us2 = divmod(us2, 256)
1847 basestate = bytes([yhi, ylo, self._month, self._day, 1949 m = self._month
1950 if self._fold and protocol > 3:
1951 m += 128
1952 basestate = bytes([yhi, ylo, m, self._day,
1848 self._hour, self._minute, self._second, 1953 self._hour, self._minute, self._second,
1849 us1, us2, us3]) 1954 us1, us2, us3])
1850 if self._tzinfo is None: 1955 if self._tzinfo is None:
1851 return (basestate,) 1956 return (basestate,)
1852 else: 1957 else:
1853 return (basestate, self._tzinfo) 1958 return (basestate, self._tzinfo)
1854 1959
1855 def __setstate(self, string, tzinfo): 1960 def __setstate(self, string, tzinfo):
1856 (yhi, ylo, self._month, self._day, self._hour, 1961 if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class):
1962 raise TypeError("bad tzinfo state arg")
1963 (yhi, ylo, m, self._day, self._hour,
1857 self._minute, self._second, us1, us2, us3) = string 1964 self._minute, self._second, us1, us2, us3) = string
1965 if m > 127:
1966 self._fold = 1
1967 self._month = m - 128
1968 else:
1969 self._fold = 0
1970 self._month = m
1858 self._year = yhi * 256 + ylo 1971 self._year = yhi * 256 + ylo
1859 self._microsecond = (((us1 << 8) | us2) << 8) | us3 1972 self._microsecond = (((us1 << 8) | us2) << 8) | us3
1860 if tzinfo is None or isinstance(tzinfo, _tzinfo_class): 1973 self._tzinfo = tzinfo
1861 self._tzinfo = tzinfo 1974
1862 else: 1975 def __reduce_ex__(self, protocol):
1863 raise TypeError("bad tzinfo state arg %r" % tzinfo) 1976 return (self.__class__, self._getstate(protocol))
1864
1865 def __reduce__(self):
1866 return (self.__class__, self._getstate())
1867 1977
1868 1978
1869 datetime.min = datetime(1, 1, 1) 1979 datetime.min = datetime(1, 1, 1)
1870 datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) 1980 datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999)
1871 datetime.resolution = timedelta(microseconds=1) 1981 datetime.resolution = timedelta(microseconds=1)
1872 1982
1873 1983
1874 def _isoweek1monday(year): 1984 def _isoweek1monday(year):
1875 # Helper to calculate the day number of the Monday starting week 1 1985 # Helper to calculate the day number of the Monday starting week 1
1876 # XXX This could be done more efficiently 1986 # XXX This could be done more efficiently
1877 THURSDAY = 3 1987 THURSDAY = 3
1878 firstday = _ymd2ord(year, 1, 1) 1988 firstday = _ymd2ord(year, 1, 1)
1879 firstweekday = (firstday + 6) % 7 # See weekday() above 1989 firstweekday = (firstday + 6) % 7 # See weekday() above
1880 week1monday = firstday - firstweekday 1990 week1monday = firstday - firstweekday
1881 if firstweekday > THURSDAY: 1991 if firstweekday > THURSDAY:
1882 week1monday += 7 1992 week1monday += 7
1883 return week1monday 1993 return week1monday
1884 1994
1885 class timezone(tzinfo): 1995 class timezone(tzinfo):
1886 __slots__ = '_offset', '_name' 1996 __slots__ = '_offset', '_name'
1887 1997
1888 # Sentinel value to disallow None 1998 # Sentinel value to disallow None
1889 _Omitted = object() 1999 _Omitted = object()
1890 def __new__(cls, offset, name=_Omitted): 2000 def __new__(cls, offset, name=_Omitted):
1891 if isinstance(offset, str):
1892 if name is not cls._Omitted:
1893 raise ValueError("name with str offset is not allowed")
1894 if offset == 'Z':
1895 return cls.utc
1896 elif len(offset) == 5:
1897 return cls(timedelta(hours=int(offset[:3]),
1898 minutes=int(offset[3:])))
1899 elif len(offset) == 6:
1900 return cls(timedelta(hours=int(offset[:3]),
1901 minutes=int(offset[4:])))
1902 else:
1903 raise ValueError("invalid timezone string")
1904
1905 if not isinstance(offset, timedelta): 2001 if not isinstance(offset, timedelta):
1906 raise TypeError("offset must be a timedelta or str") 2002 raise TypeError("offset must be a timedelta")
1907 if name is cls._Omitted: 2003 if name is cls._Omitted:
1908 if not offset: 2004 if not offset:
1909 return cls.utc 2005 return cls.utc
1910 name = None 2006 name = None
1911 elif not isinstance(name, str): 2007 elif not isinstance(name, str):
1912 raise TypeError("name must be a string") 2008 raise TypeError("name must be a string")
1913 if not cls._minoffset <= offset <= cls._maxoffset: 2009 if not cls._minoffset <= offset <= cls._maxoffset:
1914 raise ValueError("offset must be a timedelta" 2010 raise ValueError("offset must be a timedelta "
1915 " strictly between -timedelta(hours=24) and" 2011 "strictly between -timedelta(hours=24) and "
1916 " timedelta(hours=24).") 2012 "timedelta(hours=24).")
1917 if (offset.microseconds != 0 or 2013 if (offset.microseconds != 0 or offset.seconds % 60 != 0):
1918 offset.seconds % 60 != 0): 2014 raise ValueError("offset must be a timedelta "
1919 raise ValueError("offset must be a timedelta" 2015 "representing a whole number of minutes")
1920 " representing a whole number of minutes")
1921 return cls._create(offset, name) 2016 return cls._create(offset, name)
1922 2017
1923 @classmethod 2018 @classmethod
1924 def _create(cls, offset, name=None): 2019 def _create(cls, offset, name=None):
1925 self = tzinfo.__new__(cls) 2020 self = tzinfo.__new__(cls)
1926 self._offset = offset 2021 self._offset = offset
1927 self._name = name 2022 self._name = name
1928 return self 2023 return self
1929 2024
1930 def __getinitargs__(self): 2025 def __getinitargs__(self):
1931 """pickle support""" 2026 """pickle support"""
1932 if self._name is None: 2027 if self._name is None:
1933 return (self._offset,) 2028 return (self._offset,)
1934 return (self._offset, self._name) 2029 return (self._offset, self._name)
1935 2030
1936 def __eq__(self, other): 2031 def __eq__(self, other):
2032 if type(other) != timezone:
2033 return False
1937 return self._offset == other._offset 2034 return self._offset == other._offset
1938 2035
1939 def __hash__(self): 2036 def __hash__(self):
1940 return hash(self._offset) 2037 return hash(self._offset)
1941 2038
1942 def __repr__(self): 2039 def __repr__(self):
1943 """Convert to formal string, for repr(). 2040 """Convert to formal string, for repr().
1944 2041
1945 >>> tz = timezone.utc 2042 >>> tz = timezone.utc
1946 >>> repr(tz) 2043 >>> repr(tz)
1947 'datetime.timezone.utc' 2044 'datetime.timezone.utc'
1948 >>> tz = timezone(timedelta(hours=-5), 'EST') 2045 >>> tz = timezone(timedelta(hours=-5), 'EST')
1949 >>> repr(tz) 2046 >>> repr(tz)
1950 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')" 2047 "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
1951 """ 2048 """
1952 if self is self.utc: 2049 if self is self.utc:
1953 return 'datetime.timezone.utc' 2050 return 'datetime.timezone.utc'
1954 if self._name is None: 2051 if self._name is None:
1955 return "%s(%r)" % ('datetime.' + self.__class__.__name__, 2052 return "%s.%s(%r)" % (self.__class__.__module__,
1956 self._offset) 2053 self.__class__.__qualname__,
1957 return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__, 2054 self._offset)
1958 self._offset, self._name) 2055 return "%s.%s(%r, %r)" % (self.__class__.__module__,
2056 self.__class__.__qualname__,
2057 self._offset, self._name)
1959 2058
1960 def __str__(self): 2059 def __str__(self):
1961 return self.tzname(None) 2060 return self.tzname(None)
1962 2061
1963 def utcoffset(self, dt): 2062 def utcoffset(self, dt):
1964 if isinstance(dt, datetime) or dt is None: 2063 if isinstance(dt, datetime) or dt is None:
1965 return self._offset 2064 return self._offset
1966 raise TypeError("utcoffset() argument must be a datetime instance" 2065 raise TypeError("utcoffset() argument must be a datetime instance"
1967 " or None") 2066 " or None")
1968 2067
(...skipping 18 matching lines...) Expand all
1987 "is not self") 2086 "is not self")
1988 return dt + self._offset 2087 return dt + self._offset
1989 raise TypeError("fromutc() argument must be a datetime instance" 2088 raise TypeError("fromutc() argument must be a datetime instance"
1990 " or None") 2089 " or None")
1991 2090
1992 _maxoffset = timedelta(hours=23, minutes=59) 2091 _maxoffset = timedelta(hours=23, minutes=59)
1993 _minoffset = -_maxoffset 2092 _minoffset = -_maxoffset
1994 2093
1995 @staticmethod 2094 @staticmethod
1996 def _name_from_offset(delta): 2095 def _name_from_offset(delta):
2096 if not delta:
2097 return 'UTC'
1997 if delta < timedelta(0): 2098 if delta < timedelta(0):
1998 sign = '-' 2099 sign = '-'
1999 delta = -delta 2100 delta = -delta
2000 else: 2101 else:
2001 sign = '+' 2102 sign = '+'
2002 hours, rest = divmod(delta, timedelta(hours=1)) 2103 hours, rest = divmod(delta, timedelta(hours=1))
2003 minutes = rest // timedelta(minutes=1) 2104 minutes = rest // timedelta(minutes=1)
2004 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) 2105 return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes)
2005 2106
2006 timezone.utc = timezone._create(timedelta(0)) 2107 timezone.utc = timezone._create(timedelta(0))
2007 timezone.min = timezone._create(timezone._minoffset) 2108 timezone.min = timezone._create(timezone._minoffset)
2008 timezone.max = timezone._create(timezone._maxoffset) 2109 timezone.max = timezone._create(timezone._maxoffset)
2009 _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) 2110 _EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc)
2010 """ 2111
2011 Some time zone algebra. For a datetime x, let 2112 # Some time zone algebra. For a datetime x, let
2012 x.n = x stripped of its timezone -- its naive time. 2113 # x.n = x stripped of its timezone -- its naive time.
2013 x.o = x.utcoffset(), and assuming that doesn't raise an exception or 2114 # x.o = x.utcoffset(), and assuming that doesn't raise an exception or
2014 return None 2115 # return None
2015 x.d = x.dst(), and assuming that doesn't raise an exception or 2116 # x.d = x.dst(), and assuming that doesn't raise an exception or
2016 return None 2117 # return None
2017 x.s = x's standard offset, x.o - x.d 2118 # x.s = x's standard offset, x.o - x.d
2018 2119 #
2019 Now some derived rules, where k is a duration (timedelta). 2120 # Now some derived rules, where k is a duration (timedelta).
2020 2121 #
2021 1. x.o = x.s + x.d 2122 # 1. x.o = x.s + x.d
2022 This follows from the definition of x.s. 2123 # This follows from the definition of x.s.
2023 2124 #
2024 2. If x and y have the same tzinfo member, x.s = y.s. 2125 # 2. If x and y have the same tzinfo member, x.s = y.s.
2025 This is actually a requirement, an assumption we need to make about 2126 # This is actually a requirement, an assumption we need to make about
2026 sane tzinfo classes. 2127 # sane tzinfo classes.
2027 2128 #
2028 3. The naive UTC time corresponding to x is x.n - x.o. 2129 # 3. The naive UTC time corresponding to x is x.n - x.o.
2029 This is again a requirement for a sane tzinfo class. 2130 # This is again a requirement for a sane tzinfo class.
2030 2131 #
2031 4. (x+k).s = x.s 2132 # 4. (x+k).s = x.s
2032 This follows from #2, and that datimetimetz+timedelta preserves tzinfo. 2133 # This follows from #2, and that datimetimetz+timedelta preserves tzinfo.
2033 2134 #
2034 5. (x+k).n = x.n + k 2135 # 5. (x+k).n = x.n + k
2035 Again follows from how arithmetic is defined. 2136 # Again follows from how arithmetic is defined.
2036 2137 #
2037 Now we can explain tz.fromutc(x). Let's assume it's an interesting case 2138 # Now we can explain tz.fromutc(x). Let's assume it's an interesting case
2038 (meaning that the various tzinfo methods exist, and don't blow up or return 2139 # (meaning that the various tzinfo methods exist, and don't blow up or return
2039 None when called). 2140 # None when called).
2040 2141 #
2041 The function wants to return a datetime y with timezone tz, equivalent to x. 2142 # The function wants to return a datetime y with timezone tz, equivalent to x.
2042 x is already in UTC. 2143 # x is already in UTC.
2043 2144 #
2044 By #3, we want 2145 # By #3, we want
2045 2146 #
2046 y.n - y.o = x.n [1] 2147 # y.n - y.o = x.n [1]
2047 2148 #
2048 The algorithm starts by attaching tz to x.n, and calling that y. So 2149 # The algorithm starts by attaching tz to x.n, and calling that y. So
2049 x.n = y.n at the start. Then it wants to add a duration k to y, so that [1] 2150 # x.n = y.n at the start. Then it wants to add a duration k to y, so that [1]
2050 becomes true; in effect, we want to solve [2] for k: 2151 # becomes true; in effect, we want to solve [2] for k:
2051 2152 #
2052 (y+k).n - (y+k).o = x.n [2] 2153 # (y+k).n - (y+k).o = x.n [2]
2053 2154 #
2054 By #1, this is the same as 2155 # By #1, this is the same as
2055 2156 #
2056 (y+k).n - ((y+k).s + (y+k).d) = x.n [3] 2157 # (y+k).n - ((y+k).s + (y+k).d) = x.n [3]
2057 2158 #
2058 By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start. 2159 # By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start.
2059 Substituting that into [3], 2160 # Substituting that into [3],
2060 2161 #
2061 x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving 2162 # x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving
2062 k - (y+k).s - (y+k).d = 0; rearranging, 2163 # k - (y+k).s - (y+k).d = 0; rearranging,
2063 k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so 2164 # k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so
2064 k = y.s - (y+k).d 2165 # k = y.s - (y+k).d
2065 2166 #
2066 On the RHS, (y+k).d can't be computed directly, but y.s can be, and we 2167 # On the RHS, (y+k).d can't be computed directly, but y.s can be, and we
2067 approximate k by ignoring the (y+k).d term at first. Note that k can't be 2168 # approximate k by ignoring the (y+k).d term at first. Note that k can't be
2068 very large, since all offset-returning methods return a duration of magnitude 2169 # very large, since all offset-returning methods return a duration of magnitude
2069 less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must 2170 # less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must
2070 be 0, so ignoring it has no consequence then. 2171 # be 0, so ignoring it has no consequence then.
2071 2172 #
2072 In any case, the new value is 2173 # In any case, the new value is
2073 2174 #
2074 z = y + y.s [4] 2175 # z = y + y.s [4]
2075 2176 #
2076 It's helpful to step back at look at [4] from a higher level: it's simply 2177 # It's helpful to step back at look at [4] from a higher level: it's simply
2077 mapping from UTC to tz's standard time. 2178 # mapping from UTC to tz's standard time.
2078 2179 #
2079 At this point, if 2180 # At this point, if
2080 2181 #
2081 z.n - z.o = x.n [5] 2182 # z.n - z.o = x.n [5]
2082 2183 #
2083 we have an equivalent time, and are almost done. The insecurity here is 2184 # we have an equivalent time, and are almost done. The insecurity here is
2084 at the start of daylight time. Picture US Eastern for concreteness. The wall 2185 # at the start of daylight time. Picture US Eastern for concreteness. The wall
2085 time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good 2186 # time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good
2086 sense then. The docs ask that an Eastern tzinfo class consider such a time to 2187 # sense then. The docs ask that an Eastern tzinfo class consider such a time to
2087 be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST 2188 # be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST
2088 on the day DST starts. We want to return the 1:MM EST spelling because that's 2189 # on the day DST starts. We want to return the 1:MM EST spelling because that's
2089 the only spelling that makes sense on the local wall clock. 2190 # the only spelling that makes sense on the local wall clock.
2090 2191 #
2091 In fact, if [5] holds at this point, we do have the standard-time spelling, 2192 # In fact, if [5] holds at this point, we do have the standard-time spelling,
2092 but that takes a bit of proof. We first prove a stronger result. What's the 2193 # but that takes a bit of proof. We first prove a stronger result. What's the
2093 difference between the LHS and RHS of [5]? Let 2194 # difference between the LHS and RHS of [5]? Let
2094 2195 #
2095 diff = x.n - (z.n - z.o) [6] 2196 # diff = x.n - (z.n - z.o) [6]
2096 2197 #
2097 Now 2198 # Now
2098 z.n = by [4] 2199 # z.n = by [4]
2099 (y + y.s).n = by #5 2200 # (y + y.s).n = by #5
2100 y.n + y.s = since y.n = x.n 2201 # y.n + y.s = since y.n = x.n
2101 x.n + y.s = since z and y are have the same tzinfo member, 2202 # x.n + y.s = since z and y are have the same tzinfo member,
2102 y.s = z.s by #2 2203 # y.s = z.s by #2
2103 x.n + z.s 2204 # x.n + z.s
2104 2205 #
2105 Plugging that back into [6] gives 2206 # Plugging that back into [6] gives
2106 2207 #
2107 diff = 2208 # diff =
2108 x.n - ((x.n + z.s) - z.o) = expanding 2209 # x.n - ((x.n + z.s) - z.o) = expanding
2109 x.n - x.n - z.s + z.o = cancelling 2210 # x.n - x.n - z.s + z.o = cancelling
2110 - z.s + z.o = by #2 2211 # - z.s + z.o = by #2
2111 z.d 2212 # z.d
2112 2213 #
2113 So diff = z.d. 2214 # So diff = z.d.
2114 2215 #
2115 If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time 2216 # If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time
2116 spelling we wanted in the endcase described above. We're done. Contrarily, 2217 # spelling we wanted in the endcase described above. We're done. Contrarily,
2117 if z.d = 0, then we have a UTC equivalent, and are also done. 2218 # if z.d = 0, then we have a UTC equivalent, and are also done.
2118 2219 #
2119 If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to 2220 # If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to
2120 add to z (in effect, z is in tz's standard time, and we need to shift the 2221 # add to z (in effect, z is in tz's standard time, and we need to shift the
2121 local clock into tz's daylight time). 2222 # local clock into tz's daylight time).
2122 2223 #
2123 Let 2224 # Let
2124 2225 #
2125 z' = z + z.d = z + diff [7] 2226 # z' = z + z.d = z + diff [7]
2126 2227 #
2127 and we can again ask whether 2228 # and we can again ask whether
2128 2229 #
2129 z'.n - z'.o = x.n [8] 2230 # z'.n - z'.o = x.n [8]
2130 2231 #
2131 If so, we're done. If not, the tzinfo class is insane, according to the 2232 # If so, we're done. If not, the tzinfo class is insane, according to the
2132 assumptions we've made. This also requires a bit of proof. As before, let's 2233 # assumptions we've made. This also requires a bit of proof. As before, let's
2133 compute the difference between the LHS and RHS of [8] (and skipping some of 2234 # compute the difference between the LHS and RHS of [8] (and skipping some of
2134 the justifications for the kinds of substitutions we've done several times 2235 # the justifications for the kinds of substitutions we've done several times
2135 already): 2236 # already):
2136 2237 #
2137 diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7] 2238 # diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7]
2138 x.n - (z.n + diff - z'.o) = replacing diff via [6] 2239 # x.n - (z.n + diff - z'.o) = replacing diff via [6]
2139 x.n - (z.n + x.n - (z.n - z.o) - z'.o) = 2240 # x.n - (z.n + x.n - (z.n - z.o) - z'.o) =
2140 x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n 2241 # x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n
2141 - z.n + z.n - z.o + z'.o = cancel z.n 2242 # - z.n + z.n - z.o + z'.o = cancel z.n
2142 - z.o + z'.o = #1 twice 2243 # - z.o + z'.o = #1 twice
2143 -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo 2244 # -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo
2144 z'.d - z.d 2245 # z'.d - z.d
2145 2246 #
2146 So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal, 2247 # So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal,
2147 we've found the UTC-equivalent so are done. In fact, we stop with [7] and 2248 # we've found the UTC-equivalent so are done. In fact, we stop with [7] and
2148 return z', not bothering to compute z'.d. 2249 # return z', not bothering to compute z'.d.
2149 2250 #
2150 How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by 2251 # How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by
2151 a dst() offset, and starting *from* a time already in DST (we know z.d != 0), 2252 # a dst() offset, and starting *from* a time already in DST (we know z.d != 0),
2152 would have to change the result dst() returns: we start in DST, and moving 2253 # would have to change the result dst() returns: we start in DST, and moving
2153 a little further into it takes us out of DST. 2254 # a little further into it takes us out of DST.
2154 2255 #
2155 There isn't a sane case where this can happen. The closest it gets is at 2256 # There isn't a sane case where this can happen. The closest it gets is at
2156 the end of DST, where there's an hour in UTC with no spelling in a hybrid 2257 # the end of DST, where there's an hour in UTC with no spelling in a hybrid
2157 tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During 2258 # tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During
2158 that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM 2259 # that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM
2159 UTC) because the docs insist on that, but 0:MM is taken as being in daylight 2260 # UTC) because the docs insist on that, but 0:MM is taken as being in daylight
2160 time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local 2261 # time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local
2161 clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in 2262 # clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in
2162 standard time. Since that's what the local clock *does*, we want to map both 2263 # standard time. Since that's what the local clock *does*, we want to map both
2163 UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous 2264 # UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous
2164 in local time, but so it goes -- it's the way the local clock works. 2265 # in local time, but so it goes -- it's the way the local clock works.
2165 2266 #
2166 When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0, 2267 # When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0,
2167 so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going. 2268 # so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going.
2168 z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8] 2269 # z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8]
2169 (correctly) concludes that z' is not UTC-equivalent to x. 2270 # (correctly) concludes that z' is not UTC-equivalent to x.
2170 2271 #
2171 Because we know z.d said z was in daylight time (else [5] would have held and 2272 # Because we know z.d said z was in daylight time (else [5] would have held and
2172 we would have stopped then), and we know z.d != z'.d (else [8] would have held 2273 # we would have stopped then), and we know z.d != z'.d (else [8] would have held
2173 and we have stopped then), and there are only 2 possible values dst() can 2274 # and we have stopped then), and there are only 2 possible values dst() can
2174 return in Eastern, it follows that z'.d must be 0 (which it is in the example, 2275 # return in Eastern, it follows that z'.d must be 0 (which it is in the example,
2175 but the reasoning doesn't depend on the example -- it depends on there being 2276 # but the reasoning doesn't depend on the example -- it depends on there being
2176 two possible dst() outcomes, one zero and the other non-zero). Therefore 2277 # two possible dst() outcomes, one zero and the other non-zero). Therefore
2177 z' must be in standard time, and is the spelling we want in this case. 2278 # z' must be in standard time, and is the spelling we want in this case.
2178 2279 #
2179 Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is 2280 # Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is
2180 concerned (because it takes z' as being in standard time rather than the 2281 # concerned (because it takes z' as being in standard time rather than the
2181 daylight time we intend here), but returning it gives the real-life "local 2282 # daylight time we intend here), but returning it gives the real-life "local
2182 clock repeats an hour" behavior when mapping the "unspellable" UTC hour into 2283 # clock repeats an hour" behavior when mapping the "unspellable" UTC hour into
2183 tz. 2284 # tz.
2184 2285 #
2185 When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with 2286 # When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with
2186 the 1:MM standard time spelling we want. 2287 # the 1:MM standard time spelling we want.
2187 2288 #
2188 So how can this break? One of the assumptions must be violated. Two 2289 # So how can this break? One of the assumptions must be violated. Two
2189 possibilities: 2290 # possibilities:
2190 2291 #
2191 1) [2] effectively says that y.s is invariant across all y belong to a given 2292 # 1) [2] effectively says that y.s is invariant across all y belong to a given
2192 time zone. This isn't true if, for political reasons or continental drift, 2293 # time zone. This isn't true if, for political reasons or continental drift,
2193 a region decides to change its base offset from UTC. 2294 # a region decides to change its base offset from UTC.
2194 2295 #
2195 2) There may be versions of "double daylight" time where the tail end of 2296 # 2) There may be versions of "double daylight" time where the tail end of
2196 the analysis gives up a step too early. I haven't thought about that 2297 # the analysis gives up a step too early. I haven't thought about that
2197 enough to say. 2298 # enough to say.
2198 2299 #
2199 In any case, it's clear that the default fromutc() is strong enough to handle 2300 # In any case, it's clear that the default fromutc() is strong enough to handle
2200 "almost all" time zones: so long as the standard offset is invariant, it 2301 # "almost all" time zones: so long as the standard offset is invariant, it
2201 doesn't matter if daylight time transition points change from year to year, or 2302 # doesn't matter if daylight time transition points change from year to year, or
2202 if daylight time is skipped in some years; it doesn't matter how large or 2303 # if daylight time is skipped in some years; it doesn't matter how large or
2203 small dst() may get within its bounds; and it doesn't even matter if some 2304 # small dst() may get within its bounds; and it doesn't even matter if some
2204 perverse time zone returns a negative dst()). So a breaking case must be 2305 # perverse time zone returns a negative dst()). So a breaking case must be
2205 pretty bizarre, and a tzinfo subclass can override fromutc() if it is. 2306 # pretty bizarre, and a tzinfo subclass can override fromutc() if it is.
2206 """ 2307
2207 try: 2308 try:
2309 raise ImportError
2208 from _datetime import * 2310 from _datetime import *
2209 except ImportError: 2311 except ImportError:
2210 pass 2312 pass
2211 else: 2313 else:
2212 # Clean up unused names 2314 # Clean up unused names
2213 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, 2315 del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y,
2214 _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES, 2316 _DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time,
2215 _build_struct_time, _call_tzinfo_method, _check_date_fields, 2317 _check_date_fields, _check_int_field, _check_time_fields,
2216 _check_time_fields, _check_tzinfo_arg, _check_tzname, 2318 _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror,
2217 _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month, 2319 _date_class, _days_before_month, _days_before_year, _days_in_month,
2218 _days_before_year, _days_in_month, _format_time, _is_leap, 2320 _format_time, _is_leap, _isoweek1monday, _math, _ord2ymd,
2219 _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class, 2321 _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord)
2220 _wrap_strftime, _ymd2ord)
2221 # XXX Since import * above excludes names that start with _, 2322 # XXX Since import * above excludes names that start with _,
2222 # docstring does not get overwritten. In the future, it may be 2323 # docstring does not get overwritten. In the future, it may be
2223 # appropriate to maintain a single module level docstring and 2324 # appropriate to maintain a single module level docstring and
2224 # remove the following line. 2325 # remove the following line.
2225 from _datetime import __doc__ 2326 from _datetime import __doc__
LEFTRIGHT

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