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