17db96d56Sopenharmony_ci"""Concrete date/time and related types. 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciSee http://www.iana.org/time-zones/repository/tz-link.html for 47db96d56Sopenharmony_citime zone and DST data sources. 57db96d56Sopenharmony_ci""" 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_ci__all__ = ("date", "datetime", "time", "timedelta", "timezone", "tzinfo", 87db96d56Sopenharmony_ci "MINYEAR", "MAXYEAR", "UTC") 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ciimport time as _time 127db96d56Sopenharmony_ciimport math as _math 137db96d56Sopenharmony_ciimport sys 147db96d56Sopenharmony_cifrom operator import index as _index 157db96d56Sopenharmony_ci 167db96d56Sopenharmony_cidef _cmp(x, y): 177db96d56Sopenharmony_ci return 0 if x == y else 1 if x > y else -1 187db96d56Sopenharmony_ci 197db96d56Sopenharmony_ciMINYEAR = 1 207db96d56Sopenharmony_ciMAXYEAR = 9999 217db96d56Sopenharmony_ci_MAXORDINAL = 3652059 # date.max.toordinal() 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci# Utility functions, adapted from Python's Demo/classes/Dates.py, which 247db96d56Sopenharmony_ci# also assumes the current Gregorian calendar indefinitely extended in 257db96d56Sopenharmony_ci# both directions. Difference: Dates.py calls January 1 of year 0 day 267db96d56Sopenharmony_ci# number 1. The code here calls January 1 of year 1 day number 1. This is 277db96d56Sopenharmony_ci# to match the definition of the "proleptic Gregorian" calendar in Dershowitz 287db96d56Sopenharmony_ci# and Reingold's "Calendrical Calculations", where it's the base calendar 297db96d56Sopenharmony_ci# for all computations. See the book for algorithms for converting between 307db96d56Sopenharmony_ci# proleptic Gregorian ordinals and many other calendar systems. 317db96d56Sopenharmony_ci 327db96d56Sopenharmony_ci# -1 is a placeholder for indexing purposes. 337db96d56Sopenharmony_ci_DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] 347db96d56Sopenharmony_ci 357db96d56Sopenharmony_ci_DAYS_BEFORE_MONTH = [-1] # -1 is a placeholder for indexing purposes. 367db96d56Sopenharmony_cidbm = 0 377db96d56Sopenharmony_cifor dim in _DAYS_IN_MONTH[1:]: 387db96d56Sopenharmony_ci _DAYS_BEFORE_MONTH.append(dbm) 397db96d56Sopenharmony_ci dbm += dim 407db96d56Sopenharmony_cidel dbm, dim 417db96d56Sopenharmony_ci 427db96d56Sopenharmony_cidef _is_leap(year): 437db96d56Sopenharmony_ci "year -> 1 if leap year, else 0." 447db96d56Sopenharmony_ci return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) 457db96d56Sopenharmony_ci 467db96d56Sopenharmony_cidef _days_before_year(year): 477db96d56Sopenharmony_ci "year -> number of days before January 1st of year." 487db96d56Sopenharmony_ci y = year - 1 497db96d56Sopenharmony_ci return y*365 + y//4 - y//100 + y//400 507db96d56Sopenharmony_ci 517db96d56Sopenharmony_cidef _days_in_month(year, month): 527db96d56Sopenharmony_ci "year, month -> number of days in that month in that year." 537db96d56Sopenharmony_ci assert 1 <= month <= 12, month 547db96d56Sopenharmony_ci if month == 2 and _is_leap(year): 557db96d56Sopenharmony_ci return 29 567db96d56Sopenharmony_ci return _DAYS_IN_MONTH[month] 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_cidef _days_before_month(year, month): 597db96d56Sopenharmony_ci "year, month -> number of days in year preceding first day of month." 607db96d56Sopenharmony_ci assert 1 <= month <= 12, 'month must be in 1..12' 617db96d56Sopenharmony_ci return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) 627db96d56Sopenharmony_ci 637db96d56Sopenharmony_cidef _ymd2ord(year, month, day): 647db96d56Sopenharmony_ci "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." 657db96d56Sopenharmony_ci assert 1 <= month <= 12, 'month must be in 1..12' 667db96d56Sopenharmony_ci dim = _days_in_month(year, month) 677db96d56Sopenharmony_ci assert 1 <= day <= dim, ('day must be in 1..%d' % dim) 687db96d56Sopenharmony_ci return (_days_before_year(year) + 697db96d56Sopenharmony_ci _days_before_month(year, month) + 707db96d56Sopenharmony_ci day) 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_ci_DI400Y = _days_before_year(401) # number of days in 400 years 737db96d56Sopenharmony_ci_DI100Y = _days_before_year(101) # " " " " 100 " 747db96d56Sopenharmony_ci_DI4Y = _days_before_year(5) # " " " " 4 " 757db96d56Sopenharmony_ci 767db96d56Sopenharmony_ci# A 4-year cycle has an extra leap day over what we'd get from pasting 777db96d56Sopenharmony_ci# together 4 single years. 787db96d56Sopenharmony_ciassert _DI4Y == 4 * 365 + 1 797db96d56Sopenharmony_ci 807db96d56Sopenharmony_ci# Similarly, a 400-year cycle has an extra leap day over what we'd get from 817db96d56Sopenharmony_ci# pasting together 4 100-year cycles. 827db96d56Sopenharmony_ciassert _DI400Y == 4 * _DI100Y + 1 837db96d56Sopenharmony_ci 847db96d56Sopenharmony_ci# OTOH, a 100-year cycle has one fewer leap day than we'd get from 857db96d56Sopenharmony_ci# pasting together 25 4-year cycles. 867db96d56Sopenharmony_ciassert _DI100Y == 25 * _DI4Y - 1 877db96d56Sopenharmony_ci 887db96d56Sopenharmony_cidef _ord2ymd(n): 897db96d56Sopenharmony_ci "ordinal -> (year, month, day), considering 01-Jan-0001 as day 1." 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_ci # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years 927db96d56Sopenharmony_ci # repeats exactly every 400 years. The basic strategy is to find the 937db96d56Sopenharmony_ci # closest 400-year boundary at or before n, then work with the offset 947db96d56Sopenharmony_ci # from that boundary to n. Life is much clearer if we subtract 1 from 957db96d56Sopenharmony_ci # n first -- then the values of n at 400-year boundaries are exactly 967db96d56Sopenharmony_ci # those divisible by _DI400Y: 977db96d56Sopenharmony_ci # 987db96d56Sopenharmony_ci # D M Y n n-1 997db96d56Sopenharmony_ci # -- --- ---- ---------- ---------------- 1007db96d56Sopenharmony_ci # 31 Dec -400 -_DI400Y -_DI400Y -1 1017db96d56Sopenharmony_ci # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary 1027db96d56Sopenharmony_ci # ... 1037db96d56Sopenharmony_ci # 30 Dec 000 -1 -2 1047db96d56Sopenharmony_ci # 31 Dec 000 0 -1 1057db96d56Sopenharmony_ci # 1 Jan 001 1 0 400-year boundary 1067db96d56Sopenharmony_ci # 2 Jan 001 2 1 1077db96d56Sopenharmony_ci # 3 Jan 001 3 2 1087db96d56Sopenharmony_ci # ... 1097db96d56Sopenharmony_ci # 31 Dec 400 _DI400Y _DI400Y -1 1107db96d56Sopenharmony_ci # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary 1117db96d56Sopenharmony_ci n -= 1 1127db96d56Sopenharmony_ci n400, n = divmod(n, _DI400Y) 1137db96d56Sopenharmony_ci year = n400 * 400 + 1 # ..., -399, 1, 401, ... 1147db96d56Sopenharmony_ci 1157db96d56Sopenharmony_ci # Now n is the (non-negative) offset, in days, from January 1 of year, to 1167db96d56Sopenharmony_ci # the desired date. Now compute how many 100-year cycles precede n. 1177db96d56Sopenharmony_ci # Note that it's possible for n100 to equal 4! In that case 4 full 1187db96d56Sopenharmony_ci # 100-year cycles precede the desired day, which implies the desired 1197db96d56Sopenharmony_ci # day is December 31 at the end of a 400-year cycle. 1207db96d56Sopenharmony_ci n100, n = divmod(n, _DI100Y) 1217db96d56Sopenharmony_ci 1227db96d56Sopenharmony_ci # Now compute how many 4-year cycles precede it. 1237db96d56Sopenharmony_ci n4, n = divmod(n, _DI4Y) 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_ci # And now how many single years. Again n1 can be 4, and again meaning 1267db96d56Sopenharmony_ci # that the desired day is December 31 at the end of the 4-year cycle. 1277db96d56Sopenharmony_ci n1, n = divmod(n, 365) 1287db96d56Sopenharmony_ci 1297db96d56Sopenharmony_ci year += n100 * 100 + n4 * 4 + n1 1307db96d56Sopenharmony_ci if n1 == 4 or n100 == 4: 1317db96d56Sopenharmony_ci assert n == 0 1327db96d56Sopenharmony_ci return year-1, 12, 31 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci # Now the year is correct, and n is the offset from January 1. We find 1357db96d56Sopenharmony_ci # the month via an estimate that's either exact or one too large. 1367db96d56Sopenharmony_ci leapyear = n1 == 3 and (n4 != 24 or n100 == 3) 1377db96d56Sopenharmony_ci assert leapyear == _is_leap(year) 1387db96d56Sopenharmony_ci month = (n + 50) >> 5 1397db96d56Sopenharmony_ci preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear) 1407db96d56Sopenharmony_ci if preceding > n: # estimate is too large 1417db96d56Sopenharmony_ci month -= 1 1427db96d56Sopenharmony_ci preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear) 1437db96d56Sopenharmony_ci n -= preceding 1447db96d56Sopenharmony_ci assert 0 <= n < _days_in_month(year, month) 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ci # Now the year and month are correct, and n is the offset from the 1477db96d56Sopenharmony_ci # start of that month: we're done! 1487db96d56Sopenharmony_ci return year, month, n+1 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_ci# Month and day names. For localized versions, see the calendar module. 1517db96d56Sopenharmony_ci_MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", 1527db96d56Sopenharmony_ci "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 1537db96d56Sopenharmony_ci_DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 1547db96d56Sopenharmony_ci 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_cidef _build_struct_time(y, m, d, hh, mm, ss, dstflag): 1577db96d56Sopenharmony_ci wday = (_ymd2ord(y, m, d) + 6) % 7 1587db96d56Sopenharmony_ci dnum = _days_before_month(y, m) + d 1597db96d56Sopenharmony_ci return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) 1607db96d56Sopenharmony_ci 1617db96d56Sopenharmony_cidef _format_time(hh, mm, ss, us, timespec='auto'): 1627db96d56Sopenharmony_ci specs = { 1637db96d56Sopenharmony_ci 'hours': '{:02d}', 1647db96d56Sopenharmony_ci 'minutes': '{:02d}:{:02d}', 1657db96d56Sopenharmony_ci 'seconds': '{:02d}:{:02d}:{:02d}', 1667db96d56Sopenharmony_ci 'milliseconds': '{:02d}:{:02d}:{:02d}.{:03d}', 1677db96d56Sopenharmony_ci 'microseconds': '{:02d}:{:02d}:{:02d}.{:06d}' 1687db96d56Sopenharmony_ci } 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci if timespec == 'auto': 1717db96d56Sopenharmony_ci # Skip trailing microseconds when us==0. 1727db96d56Sopenharmony_ci timespec = 'microseconds' if us else 'seconds' 1737db96d56Sopenharmony_ci elif timespec == 'milliseconds': 1747db96d56Sopenharmony_ci us //= 1000 1757db96d56Sopenharmony_ci try: 1767db96d56Sopenharmony_ci fmt = specs[timespec] 1777db96d56Sopenharmony_ci except KeyError: 1787db96d56Sopenharmony_ci raise ValueError('Unknown timespec value') 1797db96d56Sopenharmony_ci else: 1807db96d56Sopenharmony_ci return fmt.format(hh, mm, ss, us) 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_cidef _format_offset(off): 1837db96d56Sopenharmony_ci s = '' 1847db96d56Sopenharmony_ci if off is not None: 1857db96d56Sopenharmony_ci if off.days < 0: 1867db96d56Sopenharmony_ci sign = "-" 1877db96d56Sopenharmony_ci off = -off 1887db96d56Sopenharmony_ci else: 1897db96d56Sopenharmony_ci sign = "+" 1907db96d56Sopenharmony_ci hh, mm = divmod(off, timedelta(hours=1)) 1917db96d56Sopenharmony_ci mm, ss = divmod(mm, timedelta(minutes=1)) 1927db96d56Sopenharmony_ci s += "%s%02d:%02d" % (sign, hh, mm) 1937db96d56Sopenharmony_ci if ss or ss.microseconds: 1947db96d56Sopenharmony_ci s += ":%02d" % ss.seconds 1957db96d56Sopenharmony_ci 1967db96d56Sopenharmony_ci if ss.microseconds: 1977db96d56Sopenharmony_ci s += '.%06d' % ss.microseconds 1987db96d56Sopenharmony_ci return s 1997db96d56Sopenharmony_ci 2007db96d56Sopenharmony_ci# Correctly substitute for %z and %Z escapes in strftime formats. 2017db96d56Sopenharmony_cidef _wrap_strftime(object, format, timetuple): 2027db96d56Sopenharmony_ci # Don't call utcoffset() or tzname() unless actually needed. 2037db96d56Sopenharmony_ci freplace = None # the string to use for %f 2047db96d56Sopenharmony_ci zreplace = None # the string to use for %z 2057db96d56Sopenharmony_ci Zreplace = None # the string to use for %Z 2067db96d56Sopenharmony_ci 2077db96d56Sopenharmony_ci # Scan format for %z and %Z escapes, replacing as needed. 2087db96d56Sopenharmony_ci newformat = [] 2097db96d56Sopenharmony_ci push = newformat.append 2107db96d56Sopenharmony_ci i, n = 0, len(format) 2117db96d56Sopenharmony_ci while i < n: 2127db96d56Sopenharmony_ci ch = format[i] 2137db96d56Sopenharmony_ci i += 1 2147db96d56Sopenharmony_ci if ch == '%': 2157db96d56Sopenharmony_ci if i < n: 2167db96d56Sopenharmony_ci ch = format[i] 2177db96d56Sopenharmony_ci i += 1 2187db96d56Sopenharmony_ci if ch == 'f': 2197db96d56Sopenharmony_ci if freplace is None: 2207db96d56Sopenharmony_ci freplace = '%06d' % getattr(object, 2217db96d56Sopenharmony_ci 'microsecond', 0) 2227db96d56Sopenharmony_ci newformat.append(freplace) 2237db96d56Sopenharmony_ci elif ch == 'z': 2247db96d56Sopenharmony_ci if zreplace is None: 2257db96d56Sopenharmony_ci zreplace = "" 2267db96d56Sopenharmony_ci if hasattr(object, "utcoffset"): 2277db96d56Sopenharmony_ci offset = object.utcoffset() 2287db96d56Sopenharmony_ci if offset is not None: 2297db96d56Sopenharmony_ci sign = '+' 2307db96d56Sopenharmony_ci if offset.days < 0: 2317db96d56Sopenharmony_ci offset = -offset 2327db96d56Sopenharmony_ci sign = '-' 2337db96d56Sopenharmony_ci h, rest = divmod(offset, timedelta(hours=1)) 2347db96d56Sopenharmony_ci m, rest = divmod(rest, timedelta(minutes=1)) 2357db96d56Sopenharmony_ci s = rest.seconds 2367db96d56Sopenharmony_ci u = offset.microseconds 2377db96d56Sopenharmony_ci if u: 2387db96d56Sopenharmony_ci zreplace = '%c%02d%02d%02d.%06d' % (sign, h, m, s, u) 2397db96d56Sopenharmony_ci elif s: 2407db96d56Sopenharmony_ci zreplace = '%c%02d%02d%02d' % (sign, h, m, s) 2417db96d56Sopenharmony_ci else: 2427db96d56Sopenharmony_ci zreplace = '%c%02d%02d' % (sign, h, m) 2437db96d56Sopenharmony_ci assert '%' not in zreplace 2447db96d56Sopenharmony_ci newformat.append(zreplace) 2457db96d56Sopenharmony_ci elif ch == 'Z': 2467db96d56Sopenharmony_ci if Zreplace is None: 2477db96d56Sopenharmony_ci Zreplace = "" 2487db96d56Sopenharmony_ci if hasattr(object, "tzname"): 2497db96d56Sopenharmony_ci s = object.tzname() 2507db96d56Sopenharmony_ci if s is not None: 2517db96d56Sopenharmony_ci # strftime is going to have at this: escape % 2527db96d56Sopenharmony_ci Zreplace = s.replace('%', '%%') 2537db96d56Sopenharmony_ci newformat.append(Zreplace) 2547db96d56Sopenharmony_ci else: 2557db96d56Sopenharmony_ci push('%') 2567db96d56Sopenharmony_ci push(ch) 2577db96d56Sopenharmony_ci else: 2587db96d56Sopenharmony_ci push('%') 2597db96d56Sopenharmony_ci else: 2607db96d56Sopenharmony_ci push(ch) 2617db96d56Sopenharmony_ci newformat = "".join(newformat) 2627db96d56Sopenharmony_ci return _time.strftime(newformat, timetuple) 2637db96d56Sopenharmony_ci 2647db96d56Sopenharmony_ci# Helpers for parsing the result of isoformat() 2657db96d56Sopenharmony_cidef _is_ascii_digit(c): 2667db96d56Sopenharmony_ci return c in "0123456789" 2677db96d56Sopenharmony_ci 2687db96d56Sopenharmony_cidef _find_isoformat_datetime_separator(dtstr): 2697db96d56Sopenharmony_ci # See the comment in _datetimemodule.c:_find_isoformat_datetime_separator 2707db96d56Sopenharmony_ci len_dtstr = len(dtstr) 2717db96d56Sopenharmony_ci if len_dtstr == 7: 2727db96d56Sopenharmony_ci return 7 2737db96d56Sopenharmony_ci 2747db96d56Sopenharmony_ci assert len_dtstr > 7 2757db96d56Sopenharmony_ci date_separator = "-" 2767db96d56Sopenharmony_ci week_indicator = "W" 2777db96d56Sopenharmony_ci 2787db96d56Sopenharmony_ci if dtstr[4] == date_separator: 2797db96d56Sopenharmony_ci if dtstr[5] == week_indicator: 2807db96d56Sopenharmony_ci if len_dtstr < 8: 2817db96d56Sopenharmony_ci raise ValueError("Invalid ISO string") 2827db96d56Sopenharmony_ci if len_dtstr > 8 and dtstr[8] == date_separator: 2837db96d56Sopenharmony_ci if len_dtstr == 9: 2847db96d56Sopenharmony_ci raise ValueError("Invalid ISO string") 2857db96d56Sopenharmony_ci if len_dtstr > 10 and _is_ascii_digit(dtstr[10]): 2867db96d56Sopenharmony_ci # This is as far as we need to resolve the ambiguity for 2877db96d56Sopenharmony_ci # the moment - if we have YYYY-Www-##, the separator is 2887db96d56Sopenharmony_ci # either a hyphen at 8 or a number at 10. 2897db96d56Sopenharmony_ci # 2907db96d56Sopenharmony_ci # We'll assume it's a hyphen at 8 because it's way more 2917db96d56Sopenharmony_ci # likely that someone will use a hyphen as a separator than 2927db96d56Sopenharmony_ci # a number, but at this point it's really best effort 2937db96d56Sopenharmony_ci # because this is an extension of the spec anyway. 2947db96d56Sopenharmony_ci # TODO(pganssle): Document this 2957db96d56Sopenharmony_ci return 8 2967db96d56Sopenharmony_ci return 10 2977db96d56Sopenharmony_ci else: 2987db96d56Sopenharmony_ci # YYYY-Www (8) 2997db96d56Sopenharmony_ci return 8 3007db96d56Sopenharmony_ci else: 3017db96d56Sopenharmony_ci # YYYY-MM-DD (10) 3027db96d56Sopenharmony_ci return 10 3037db96d56Sopenharmony_ci else: 3047db96d56Sopenharmony_ci if dtstr[4] == week_indicator: 3057db96d56Sopenharmony_ci # YYYYWww (7) or YYYYWwwd (8) 3067db96d56Sopenharmony_ci idx = 7 3077db96d56Sopenharmony_ci while idx < len_dtstr: 3087db96d56Sopenharmony_ci if not _is_ascii_digit(dtstr[idx]): 3097db96d56Sopenharmony_ci break 3107db96d56Sopenharmony_ci idx += 1 3117db96d56Sopenharmony_ci 3127db96d56Sopenharmony_ci if idx < 9: 3137db96d56Sopenharmony_ci return idx 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ci if idx % 2 == 0: 3167db96d56Sopenharmony_ci # If the index of the last number is even, it's YYYYWwwd 3177db96d56Sopenharmony_ci return 7 3187db96d56Sopenharmony_ci else: 3197db96d56Sopenharmony_ci return 8 3207db96d56Sopenharmony_ci else: 3217db96d56Sopenharmony_ci # YYYYMMDD (8) 3227db96d56Sopenharmony_ci return 8 3237db96d56Sopenharmony_ci 3247db96d56Sopenharmony_ci 3257db96d56Sopenharmony_cidef _parse_isoformat_date(dtstr): 3267db96d56Sopenharmony_ci # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10, 3277db96d56Sopenharmony_ci # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator 3287db96d56Sopenharmony_ci assert len(dtstr) in (7, 8, 10) 3297db96d56Sopenharmony_ci year = int(dtstr[0:4]) 3307db96d56Sopenharmony_ci has_sep = dtstr[4] == '-' 3317db96d56Sopenharmony_ci 3327db96d56Sopenharmony_ci pos = 4 + has_sep 3337db96d56Sopenharmony_ci if dtstr[pos:pos + 1] == "W": 3347db96d56Sopenharmony_ci # YYYY-?Www-?D? 3357db96d56Sopenharmony_ci pos += 1 3367db96d56Sopenharmony_ci weekno = int(dtstr[pos:pos + 2]) 3377db96d56Sopenharmony_ci pos += 2 3387db96d56Sopenharmony_ci 3397db96d56Sopenharmony_ci dayno = 1 3407db96d56Sopenharmony_ci if len(dtstr) > pos: 3417db96d56Sopenharmony_ci if (dtstr[pos:pos + 1] == '-') != has_sep: 3427db96d56Sopenharmony_ci raise ValueError("Inconsistent use of dash separator") 3437db96d56Sopenharmony_ci 3447db96d56Sopenharmony_ci pos += has_sep 3457db96d56Sopenharmony_ci 3467db96d56Sopenharmony_ci dayno = int(dtstr[pos:pos + 1]) 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci return list(_isoweek_to_gregorian(year, weekno, dayno)) 3497db96d56Sopenharmony_ci else: 3507db96d56Sopenharmony_ci month = int(dtstr[pos:pos + 2]) 3517db96d56Sopenharmony_ci pos += 2 3527db96d56Sopenharmony_ci if (dtstr[pos:pos + 1] == "-") != has_sep: 3537db96d56Sopenharmony_ci raise ValueError("Inconsistent use of dash separator") 3547db96d56Sopenharmony_ci 3557db96d56Sopenharmony_ci pos += has_sep 3567db96d56Sopenharmony_ci day = int(dtstr[pos:pos + 2]) 3577db96d56Sopenharmony_ci 3587db96d56Sopenharmony_ci return [year, month, day] 3597db96d56Sopenharmony_ci 3607db96d56Sopenharmony_ci 3617db96d56Sopenharmony_ci_FRACTION_CORRECTION = [100000, 10000, 1000, 100, 10] 3627db96d56Sopenharmony_ci 3637db96d56Sopenharmony_ci 3647db96d56Sopenharmony_cidef _parse_hh_mm_ss_ff(tstr): 3657db96d56Sopenharmony_ci # Parses things of the form HH[:?MM[:?SS[{.,}fff[fff]]]] 3667db96d56Sopenharmony_ci len_str = len(tstr) 3677db96d56Sopenharmony_ci 3687db96d56Sopenharmony_ci time_comps = [0, 0, 0, 0] 3697db96d56Sopenharmony_ci pos = 0 3707db96d56Sopenharmony_ci for comp in range(0, 3): 3717db96d56Sopenharmony_ci if (len_str - pos) < 2: 3727db96d56Sopenharmony_ci raise ValueError("Incomplete time component") 3737db96d56Sopenharmony_ci 3747db96d56Sopenharmony_ci time_comps[comp] = int(tstr[pos:pos+2]) 3757db96d56Sopenharmony_ci 3767db96d56Sopenharmony_ci pos += 2 3777db96d56Sopenharmony_ci next_char = tstr[pos:pos+1] 3787db96d56Sopenharmony_ci 3797db96d56Sopenharmony_ci if comp == 0: 3807db96d56Sopenharmony_ci has_sep = next_char == ':' 3817db96d56Sopenharmony_ci 3827db96d56Sopenharmony_ci if not next_char or comp >= 2: 3837db96d56Sopenharmony_ci break 3847db96d56Sopenharmony_ci 3857db96d56Sopenharmony_ci if has_sep and next_char != ':': 3867db96d56Sopenharmony_ci raise ValueError("Invalid time separator: %c" % next_char) 3877db96d56Sopenharmony_ci 3887db96d56Sopenharmony_ci pos += has_sep 3897db96d56Sopenharmony_ci 3907db96d56Sopenharmony_ci if pos < len_str: 3917db96d56Sopenharmony_ci if tstr[pos] not in '.,': 3927db96d56Sopenharmony_ci raise ValueError("Invalid microsecond component") 3937db96d56Sopenharmony_ci else: 3947db96d56Sopenharmony_ci pos += 1 3957db96d56Sopenharmony_ci 3967db96d56Sopenharmony_ci len_remainder = len_str - pos 3977db96d56Sopenharmony_ci 3987db96d56Sopenharmony_ci if len_remainder >= 6: 3997db96d56Sopenharmony_ci to_parse = 6 4007db96d56Sopenharmony_ci else: 4017db96d56Sopenharmony_ci to_parse = len_remainder 4027db96d56Sopenharmony_ci 4037db96d56Sopenharmony_ci time_comps[3] = int(tstr[pos:(pos+to_parse)]) 4047db96d56Sopenharmony_ci if to_parse < 6: 4057db96d56Sopenharmony_ci time_comps[3] *= _FRACTION_CORRECTION[to_parse-1] 4067db96d56Sopenharmony_ci if (len_remainder > to_parse 4077db96d56Sopenharmony_ci and not all(map(_is_ascii_digit, tstr[(pos+to_parse):]))): 4087db96d56Sopenharmony_ci raise ValueError("Non-digit values in unparsed fraction") 4097db96d56Sopenharmony_ci 4107db96d56Sopenharmony_ci return time_comps 4117db96d56Sopenharmony_ci 4127db96d56Sopenharmony_cidef _parse_isoformat_time(tstr): 4137db96d56Sopenharmony_ci # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] 4147db96d56Sopenharmony_ci len_str = len(tstr) 4157db96d56Sopenharmony_ci if len_str < 2: 4167db96d56Sopenharmony_ci raise ValueError("Isoformat time too short") 4177db96d56Sopenharmony_ci 4187db96d56Sopenharmony_ci # This is equivalent to re.search('[+-Z]', tstr), but faster 4197db96d56Sopenharmony_ci tz_pos = (tstr.find('-') + 1 or tstr.find('+') + 1 or tstr.find('Z') + 1) 4207db96d56Sopenharmony_ci timestr = tstr[:tz_pos-1] if tz_pos > 0 else tstr 4217db96d56Sopenharmony_ci 4227db96d56Sopenharmony_ci time_comps = _parse_hh_mm_ss_ff(timestr) 4237db96d56Sopenharmony_ci 4247db96d56Sopenharmony_ci tzi = None 4257db96d56Sopenharmony_ci if tz_pos == len_str and tstr[-1] == 'Z': 4267db96d56Sopenharmony_ci tzi = timezone.utc 4277db96d56Sopenharmony_ci elif tz_pos > 0: 4287db96d56Sopenharmony_ci tzstr = tstr[tz_pos:] 4297db96d56Sopenharmony_ci 4307db96d56Sopenharmony_ci # Valid time zone strings are: 4317db96d56Sopenharmony_ci # HH len: 2 4327db96d56Sopenharmony_ci # HHMM len: 4 4337db96d56Sopenharmony_ci # HH:MM len: 5 4347db96d56Sopenharmony_ci # HHMMSS len: 6 4357db96d56Sopenharmony_ci # HHMMSS.f+ len: 7+ 4367db96d56Sopenharmony_ci # HH:MM:SS len: 8 4377db96d56Sopenharmony_ci # HH:MM:SS.f+ len: 10+ 4387db96d56Sopenharmony_ci 4397db96d56Sopenharmony_ci if len(tzstr) in (0, 1, 3): 4407db96d56Sopenharmony_ci raise ValueError("Malformed time zone string") 4417db96d56Sopenharmony_ci 4427db96d56Sopenharmony_ci tz_comps = _parse_hh_mm_ss_ff(tzstr) 4437db96d56Sopenharmony_ci 4447db96d56Sopenharmony_ci if all(x == 0 for x in tz_comps): 4457db96d56Sopenharmony_ci tzi = timezone.utc 4467db96d56Sopenharmony_ci else: 4477db96d56Sopenharmony_ci tzsign = -1 if tstr[tz_pos - 1] == '-' else 1 4487db96d56Sopenharmony_ci 4497db96d56Sopenharmony_ci td = timedelta(hours=tz_comps[0], minutes=tz_comps[1], 4507db96d56Sopenharmony_ci seconds=tz_comps[2], microseconds=tz_comps[3]) 4517db96d56Sopenharmony_ci 4527db96d56Sopenharmony_ci tzi = timezone(tzsign * td) 4537db96d56Sopenharmony_ci 4547db96d56Sopenharmony_ci time_comps.append(tzi) 4557db96d56Sopenharmony_ci 4567db96d56Sopenharmony_ci return time_comps 4577db96d56Sopenharmony_ci 4587db96d56Sopenharmony_ci# tuple[int, int, int] -> tuple[int, int, int] version of date.fromisocalendar 4597db96d56Sopenharmony_cidef _isoweek_to_gregorian(year, week, day): 4607db96d56Sopenharmony_ci # Year is bounded this way because 9999-12-31 is (9999, 52, 5) 4617db96d56Sopenharmony_ci if not MINYEAR <= year <= MAXYEAR: 4627db96d56Sopenharmony_ci raise ValueError(f"Year is out of range: {year}") 4637db96d56Sopenharmony_ci 4647db96d56Sopenharmony_ci if not 0 < week < 53: 4657db96d56Sopenharmony_ci out_of_range = True 4667db96d56Sopenharmony_ci 4677db96d56Sopenharmony_ci if week == 53: 4687db96d56Sopenharmony_ci # ISO years have 53 weeks in them on years starting with a 4697db96d56Sopenharmony_ci # Thursday and leap years starting on a Wednesday 4707db96d56Sopenharmony_ci first_weekday = _ymd2ord(year, 1, 1) % 7 4717db96d56Sopenharmony_ci if (first_weekday == 4 or (first_weekday == 3 and 4727db96d56Sopenharmony_ci _is_leap(year))): 4737db96d56Sopenharmony_ci out_of_range = False 4747db96d56Sopenharmony_ci 4757db96d56Sopenharmony_ci if out_of_range: 4767db96d56Sopenharmony_ci raise ValueError(f"Invalid week: {week}") 4777db96d56Sopenharmony_ci 4787db96d56Sopenharmony_ci if not 0 < day < 8: 4797db96d56Sopenharmony_ci raise ValueError(f"Invalid weekday: {day} (range is [1, 7])") 4807db96d56Sopenharmony_ci 4817db96d56Sopenharmony_ci # Now compute the offset from (Y, 1, 1) in days: 4827db96d56Sopenharmony_ci day_offset = (week - 1) * 7 + (day - 1) 4837db96d56Sopenharmony_ci 4847db96d56Sopenharmony_ci # Calculate the ordinal day for monday, week 1 4857db96d56Sopenharmony_ci day_1 = _isoweek1monday(year) 4867db96d56Sopenharmony_ci ord_day = day_1 + day_offset 4877db96d56Sopenharmony_ci 4887db96d56Sopenharmony_ci return _ord2ymd(ord_day) 4897db96d56Sopenharmony_ci 4907db96d56Sopenharmony_ci 4917db96d56Sopenharmony_ci# Just raise TypeError if the arg isn't None or a string. 4927db96d56Sopenharmony_cidef _check_tzname(name): 4937db96d56Sopenharmony_ci if name is not None and not isinstance(name, str): 4947db96d56Sopenharmony_ci raise TypeError("tzinfo.tzname() must return None or string, " 4957db96d56Sopenharmony_ci "not '%s'" % type(name)) 4967db96d56Sopenharmony_ci 4977db96d56Sopenharmony_ci# name is the offset-producing method, "utcoffset" or "dst". 4987db96d56Sopenharmony_ci# offset is what it returned. 4997db96d56Sopenharmony_ci# If offset isn't None or timedelta, raises TypeError. 5007db96d56Sopenharmony_ci# If offset is None, returns None. 5017db96d56Sopenharmony_ci# Else offset is checked for being in range. 5027db96d56Sopenharmony_ci# If it is, its integer value is returned. Else ValueError is raised. 5037db96d56Sopenharmony_cidef _check_utc_offset(name, offset): 5047db96d56Sopenharmony_ci assert name in ("utcoffset", "dst") 5057db96d56Sopenharmony_ci if offset is None: 5067db96d56Sopenharmony_ci return 5077db96d56Sopenharmony_ci if not isinstance(offset, timedelta): 5087db96d56Sopenharmony_ci raise TypeError("tzinfo.%s() must return None " 5097db96d56Sopenharmony_ci "or timedelta, not '%s'" % (name, type(offset))) 5107db96d56Sopenharmony_ci if not -timedelta(1) < offset < timedelta(1): 5117db96d56Sopenharmony_ci raise ValueError("%s()=%s, must be strictly between " 5127db96d56Sopenharmony_ci "-timedelta(hours=24) and timedelta(hours=24)" % 5137db96d56Sopenharmony_ci (name, offset)) 5147db96d56Sopenharmony_ci 5157db96d56Sopenharmony_cidef _check_date_fields(year, month, day): 5167db96d56Sopenharmony_ci year = _index(year) 5177db96d56Sopenharmony_ci month = _index(month) 5187db96d56Sopenharmony_ci day = _index(day) 5197db96d56Sopenharmony_ci if not MINYEAR <= year <= MAXYEAR: 5207db96d56Sopenharmony_ci raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year) 5217db96d56Sopenharmony_ci if not 1 <= month <= 12: 5227db96d56Sopenharmony_ci raise ValueError('month must be in 1..12', month) 5237db96d56Sopenharmony_ci dim = _days_in_month(year, month) 5247db96d56Sopenharmony_ci if not 1 <= day <= dim: 5257db96d56Sopenharmony_ci raise ValueError('day must be in 1..%d' % dim, day) 5267db96d56Sopenharmony_ci return year, month, day 5277db96d56Sopenharmony_ci 5287db96d56Sopenharmony_cidef _check_time_fields(hour, minute, second, microsecond, fold): 5297db96d56Sopenharmony_ci hour = _index(hour) 5307db96d56Sopenharmony_ci minute = _index(minute) 5317db96d56Sopenharmony_ci second = _index(second) 5327db96d56Sopenharmony_ci microsecond = _index(microsecond) 5337db96d56Sopenharmony_ci if not 0 <= hour <= 23: 5347db96d56Sopenharmony_ci raise ValueError('hour must be in 0..23', hour) 5357db96d56Sopenharmony_ci if not 0 <= minute <= 59: 5367db96d56Sopenharmony_ci raise ValueError('minute must be in 0..59', minute) 5377db96d56Sopenharmony_ci if not 0 <= second <= 59: 5387db96d56Sopenharmony_ci raise ValueError('second must be in 0..59', second) 5397db96d56Sopenharmony_ci if not 0 <= microsecond <= 999999: 5407db96d56Sopenharmony_ci raise ValueError('microsecond must be in 0..999999', microsecond) 5417db96d56Sopenharmony_ci if fold not in (0, 1): 5427db96d56Sopenharmony_ci raise ValueError('fold must be either 0 or 1', fold) 5437db96d56Sopenharmony_ci return hour, minute, second, microsecond, fold 5447db96d56Sopenharmony_ci 5457db96d56Sopenharmony_cidef _check_tzinfo_arg(tz): 5467db96d56Sopenharmony_ci if tz is not None and not isinstance(tz, tzinfo): 5477db96d56Sopenharmony_ci raise TypeError("tzinfo argument must be None or of a tzinfo subclass") 5487db96d56Sopenharmony_ci 5497db96d56Sopenharmony_cidef _cmperror(x, y): 5507db96d56Sopenharmony_ci raise TypeError("can't compare '%s' to '%s'" % ( 5517db96d56Sopenharmony_ci type(x).__name__, type(y).__name__)) 5527db96d56Sopenharmony_ci 5537db96d56Sopenharmony_cidef _divide_and_round(a, b): 5547db96d56Sopenharmony_ci """divide a by b and round result to the nearest integer 5557db96d56Sopenharmony_ci 5567db96d56Sopenharmony_ci When the ratio is exactly half-way between two integers, 5577db96d56Sopenharmony_ci the even integer is returned. 5587db96d56Sopenharmony_ci """ 5597db96d56Sopenharmony_ci # Based on the reference implementation for divmod_near 5607db96d56Sopenharmony_ci # in Objects/longobject.c. 5617db96d56Sopenharmony_ci q, r = divmod(a, b) 5627db96d56Sopenharmony_ci # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. 5637db96d56Sopenharmony_ci # The expression r / b > 0.5 is equivalent to 2 * r > b if b is 5647db96d56Sopenharmony_ci # positive, 2 * r < b if b negative. 5657db96d56Sopenharmony_ci r *= 2 5667db96d56Sopenharmony_ci greater_than_half = r > b if b > 0 else r < b 5677db96d56Sopenharmony_ci if greater_than_half or r == b and q % 2 == 1: 5687db96d56Sopenharmony_ci q += 1 5697db96d56Sopenharmony_ci 5707db96d56Sopenharmony_ci return q 5717db96d56Sopenharmony_ci 5727db96d56Sopenharmony_ci 5737db96d56Sopenharmony_ciclass timedelta: 5747db96d56Sopenharmony_ci """Represent the difference between two datetime objects. 5757db96d56Sopenharmony_ci 5767db96d56Sopenharmony_ci Supported operators: 5777db96d56Sopenharmony_ci 5787db96d56Sopenharmony_ci - add, subtract timedelta 5797db96d56Sopenharmony_ci - unary plus, minus, abs 5807db96d56Sopenharmony_ci - compare to timedelta 5817db96d56Sopenharmony_ci - multiply, divide by int 5827db96d56Sopenharmony_ci 5837db96d56Sopenharmony_ci In addition, datetime supports subtraction of two datetime objects 5847db96d56Sopenharmony_ci returning a timedelta, and addition or subtraction of a datetime 5857db96d56Sopenharmony_ci and a timedelta giving a datetime. 5867db96d56Sopenharmony_ci 5877db96d56Sopenharmony_ci Representation: (days, seconds, microseconds). Why? Because I 5887db96d56Sopenharmony_ci felt like it. 5897db96d56Sopenharmony_ci """ 5907db96d56Sopenharmony_ci __slots__ = '_days', '_seconds', '_microseconds', '_hashcode' 5917db96d56Sopenharmony_ci 5927db96d56Sopenharmony_ci def __new__(cls, days=0, seconds=0, microseconds=0, 5937db96d56Sopenharmony_ci milliseconds=0, minutes=0, hours=0, weeks=0): 5947db96d56Sopenharmony_ci # Doing this efficiently and accurately in C is going to be difficult 5957db96d56Sopenharmony_ci # and error-prone, due to ubiquitous overflow possibilities, and that 5967db96d56Sopenharmony_ci # C double doesn't have enough bits of precision to represent 5977db96d56Sopenharmony_ci # microseconds over 10K years faithfully. The code here tries to make 5987db96d56Sopenharmony_ci # explicit where go-fast assumptions can be relied on, in order to 5997db96d56Sopenharmony_ci # guide the C implementation; it's way more convoluted than speed- 6007db96d56Sopenharmony_ci # ignoring auto-overflow-to-long idiomatic Python could be. 6017db96d56Sopenharmony_ci 6027db96d56Sopenharmony_ci # XXX Check that all inputs are ints or floats. 6037db96d56Sopenharmony_ci 6047db96d56Sopenharmony_ci # Final values, all integer. 6057db96d56Sopenharmony_ci # s and us fit in 32-bit signed ints; d isn't bounded. 6067db96d56Sopenharmony_ci d = s = us = 0 6077db96d56Sopenharmony_ci 6087db96d56Sopenharmony_ci # Normalize everything to days, seconds, microseconds. 6097db96d56Sopenharmony_ci days += weeks*7 6107db96d56Sopenharmony_ci seconds += minutes*60 + hours*3600 6117db96d56Sopenharmony_ci microseconds += milliseconds*1000 6127db96d56Sopenharmony_ci 6137db96d56Sopenharmony_ci # Get rid of all fractions, and normalize s and us. 6147db96d56Sopenharmony_ci # Take a deep breath <wink>. 6157db96d56Sopenharmony_ci if isinstance(days, float): 6167db96d56Sopenharmony_ci dayfrac, days = _math.modf(days) 6177db96d56Sopenharmony_ci daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.)) 6187db96d56Sopenharmony_ci assert daysecondswhole == int(daysecondswhole) # can't overflow 6197db96d56Sopenharmony_ci s = int(daysecondswhole) 6207db96d56Sopenharmony_ci assert days == int(days) 6217db96d56Sopenharmony_ci d = int(days) 6227db96d56Sopenharmony_ci else: 6237db96d56Sopenharmony_ci daysecondsfrac = 0.0 6247db96d56Sopenharmony_ci d = days 6257db96d56Sopenharmony_ci assert isinstance(daysecondsfrac, float) 6267db96d56Sopenharmony_ci assert abs(daysecondsfrac) <= 1.0 6277db96d56Sopenharmony_ci assert isinstance(d, int) 6287db96d56Sopenharmony_ci assert abs(s) <= 24 * 3600 6297db96d56Sopenharmony_ci # days isn't referenced again before redefinition 6307db96d56Sopenharmony_ci 6317db96d56Sopenharmony_ci if isinstance(seconds, float): 6327db96d56Sopenharmony_ci secondsfrac, seconds = _math.modf(seconds) 6337db96d56Sopenharmony_ci assert seconds == int(seconds) 6347db96d56Sopenharmony_ci seconds = int(seconds) 6357db96d56Sopenharmony_ci secondsfrac += daysecondsfrac 6367db96d56Sopenharmony_ci assert abs(secondsfrac) <= 2.0 6377db96d56Sopenharmony_ci else: 6387db96d56Sopenharmony_ci secondsfrac = daysecondsfrac 6397db96d56Sopenharmony_ci # daysecondsfrac isn't referenced again 6407db96d56Sopenharmony_ci assert isinstance(secondsfrac, float) 6417db96d56Sopenharmony_ci assert abs(secondsfrac) <= 2.0 6427db96d56Sopenharmony_ci 6437db96d56Sopenharmony_ci assert isinstance(seconds, int) 6447db96d56Sopenharmony_ci days, seconds = divmod(seconds, 24*3600) 6457db96d56Sopenharmony_ci d += days 6467db96d56Sopenharmony_ci s += int(seconds) # can't overflow 6477db96d56Sopenharmony_ci assert isinstance(s, int) 6487db96d56Sopenharmony_ci assert abs(s) <= 2 * 24 * 3600 6497db96d56Sopenharmony_ci # seconds isn't referenced again before redefinition 6507db96d56Sopenharmony_ci 6517db96d56Sopenharmony_ci usdouble = secondsfrac * 1e6 6527db96d56Sopenharmony_ci assert abs(usdouble) < 2.1e6 # exact value not critical 6537db96d56Sopenharmony_ci # secondsfrac isn't referenced again 6547db96d56Sopenharmony_ci 6557db96d56Sopenharmony_ci if isinstance(microseconds, float): 6567db96d56Sopenharmony_ci microseconds = round(microseconds + usdouble) 6577db96d56Sopenharmony_ci seconds, microseconds = divmod(microseconds, 1000000) 6587db96d56Sopenharmony_ci days, seconds = divmod(seconds, 24*3600) 6597db96d56Sopenharmony_ci d += days 6607db96d56Sopenharmony_ci s += seconds 6617db96d56Sopenharmony_ci else: 6627db96d56Sopenharmony_ci microseconds = int(microseconds) 6637db96d56Sopenharmony_ci seconds, microseconds = divmod(microseconds, 1000000) 6647db96d56Sopenharmony_ci days, seconds = divmod(seconds, 24*3600) 6657db96d56Sopenharmony_ci d += days 6667db96d56Sopenharmony_ci s += seconds 6677db96d56Sopenharmony_ci microseconds = round(microseconds + usdouble) 6687db96d56Sopenharmony_ci assert isinstance(s, int) 6697db96d56Sopenharmony_ci assert isinstance(microseconds, int) 6707db96d56Sopenharmony_ci assert abs(s) <= 3 * 24 * 3600 6717db96d56Sopenharmony_ci assert abs(microseconds) < 3.1e6 6727db96d56Sopenharmony_ci 6737db96d56Sopenharmony_ci # Just a little bit of carrying possible for microseconds and seconds. 6747db96d56Sopenharmony_ci seconds, us = divmod(microseconds, 1000000) 6757db96d56Sopenharmony_ci s += seconds 6767db96d56Sopenharmony_ci days, s = divmod(s, 24*3600) 6777db96d56Sopenharmony_ci d += days 6787db96d56Sopenharmony_ci 6797db96d56Sopenharmony_ci assert isinstance(d, int) 6807db96d56Sopenharmony_ci assert isinstance(s, int) and 0 <= s < 24*3600 6817db96d56Sopenharmony_ci assert isinstance(us, int) and 0 <= us < 1000000 6827db96d56Sopenharmony_ci 6837db96d56Sopenharmony_ci if abs(d) > 999999999: 6847db96d56Sopenharmony_ci raise OverflowError("timedelta # of days is too large: %d" % d) 6857db96d56Sopenharmony_ci 6867db96d56Sopenharmony_ci self = object.__new__(cls) 6877db96d56Sopenharmony_ci self._days = d 6887db96d56Sopenharmony_ci self._seconds = s 6897db96d56Sopenharmony_ci self._microseconds = us 6907db96d56Sopenharmony_ci self._hashcode = -1 6917db96d56Sopenharmony_ci return self 6927db96d56Sopenharmony_ci 6937db96d56Sopenharmony_ci def __repr__(self): 6947db96d56Sopenharmony_ci args = [] 6957db96d56Sopenharmony_ci if self._days: 6967db96d56Sopenharmony_ci args.append("days=%d" % self._days) 6977db96d56Sopenharmony_ci if self._seconds: 6987db96d56Sopenharmony_ci args.append("seconds=%d" % self._seconds) 6997db96d56Sopenharmony_ci if self._microseconds: 7007db96d56Sopenharmony_ci args.append("microseconds=%d" % self._microseconds) 7017db96d56Sopenharmony_ci if not args: 7027db96d56Sopenharmony_ci args.append('0') 7037db96d56Sopenharmony_ci return "%s.%s(%s)" % (self.__class__.__module__, 7047db96d56Sopenharmony_ci self.__class__.__qualname__, 7057db96d56Sopenharmony_ci ', '.join(args)) 7067db96d56Sopenharmony_ci 7077db96d56Sopenharmony_ci def __str__(self): 7087db96d56Sopenharmony_ci mm, ss = divmod(self._seconds, 60) 7097db96d56Sopenharmony_ci hh, mm = divmod(mm, 60) 7107db96d56Sopenharmony_ci s = "%d:%02d:%02d" % (hh, mm, ss) 7117db96d56Sopenharmony_ci if self._days: 7127db96d56Sopenharmony_ci def plural(n): 7137db96d56Sopenharmony_ci return n, abs(n) != 1 and "s" or "" 7147db96d56Sopenharmony_ci s = ("%d day%s, " % plural(self._days)) + s 7157db96d56Sopenharmony_ci if self._microseconds: 7167db96d56Sopenharmony_ci s = s + ".%06d" % self._microseconds 7177db96d56Sopenharmony_ci return s 7187db96d56Sopenharmony_ci 7197db96d56Sopenharmony_ci def total_seconds(self): 7207db96d56Sopenharmony_ci """Total seconds in the duration.""" 7217db96d56Sopenharmony_ci return ((self.days * 86400 + self.seconds) * 10**6 + 7227db96d56Sopenharmony_ci self.microseconds) / 10**6 7237db96d56Sopenharmony_ci 7247db96d56Sopenharmony_ci # Read-only field accessors 7257db96d56Sopenharmony_ci @property 7267db96d56Sopenharmony_ci def days(self): 7277db96d56Sopenharmony_ci """days""" 7287db96d56Sopenharmony_ci return self._days 7297db96d56Sopenharmony_ci 7307db96d56Sopenharmony_ci @property 7317db96d56Sopenharmony_ci def seconds(self): 7327db96d56Sopenharmony_ci """seconds""" 7337db96d56Sopenharmony_ci return self._seconds 7347db96d56Sopenharmony_ci 7357db96d56Sopenharmony_ci @property 7367db96d56Sopenharmony_ci def microseconds(self): 7377db96d56Sopenharmony_ci """microseconds""" 7387db96d56Sopenharmony_ci return self._microseconds 7397db96d56Sopenharmony_ci 7407db96d56Sopenharmony_ci def __add__(self, other): 7417db96d56Sopenharmony_ci if isinstance(other, timedelta): 7427db96d56Sopenharmony_ci # for CPython compatibility, we cannot use 7437db96d56Sopenharmony_ci # our __class__ here, but need a real timedelta 7447db96d56Sopenharmony_ci return timedelta(self._days + other._days, 7457db96d56Sopenharmony_ci self._seconds + other._seconds, 7467db96d56Sopenharmony_ci self._microseconds + other._microseconds) 7477db96d56Sopenharmony_ci return NotImplemented 7487db96d56Sopenharmony_ci 7497db96d56Sopenharmony_ci __radd__ = __add__ 7507db96d56Sopenharmony_ci 7517db96d56Sopenharmony_ci def __sub__(self, other): 7527db96d56Sopenharmony_ci if isinstance(other, timedelta): 7537db96d56Sopenharmony_ci # for CPython compatibility, we cannot use 7547db96d56Sopenharmony_ci # our __class__ here, but need a real timedelta 7557db96d56Sopenharmony_ci return timedelta(self._days - other._days, 7567db96d56Sopenharmony_ci self._seconds - other._seconds, 7577db96d56Sopenharmony_ci self._microseconds - other._microseconds) 7587db96d56Sopenharmony_ci return NotImplemented 7597db96d56Sopenharmony_ci 7607db96d56Sopenharmony_ci def __rsub__(self, other): 7617db96d56Sopenharmony_ci if isinstance(other, timedelta): 7627db96d56Sopenharmony_ci return -self + other 7637db96d56Sopenharmony_ci return NotImplemented 7647db96d56Sopenharmony_ci 7657db96d56Sopenharmony_ci def __neg__(self): 7667db96d56Sopenharmony_ci # for CPython compatibility, we cannot use 7677db96d56Sopenharmony_ci # our __class__ here, but need a real timedelta 7687db96d56Sopenharmony_ci return timedelta(-self._days, 7697db96d56Sopenharmony_ci -self._seconds, 7707db96d56Sopenharmony_ci -self._microseconds) 7717db96d56Sopenharmony_ci 7727db96d56Sopenharmony_ci def __pos__(self): 7737db96d56Sopenharmony_ci return self 7747db96d56Sopenharmony_ci 7757db96d56Sopenharmony_ci def __abs__(self): 7767db96d56Sopenharmony_ci if self._days < 0: 7777db96d56Sopenharmony_ci return -self 7787db96d56Sopenharmony_ci else: 7797db96d56Sopenharmony_ci return self 7807db96d56Sopenharmony_ci 7817db96d56Sopenharmony_ci def __mul__(self, other): 7827db96d56Sopenharmony_ci if isinstance(other, int): 7837db96d56Sopenharmony_ci # for CPython compatibility, we cannot use 7847db96d56Sopenharmony_ci # our __class__ here, but need a real timedelta 7857db96d56Sopenharmony_ci return timedelta(self._days * other, 7867db96d56Sopenharmony_ci self._seconds * other, 7877db96d56Sopenharmony_ci self._microseconds * other) 7887db96d56Sopenharmony_ci if isinstance(other, float): 7897db96d56Sopenharmony_ci usec = self._to_microseconds() 7907db96d56Sopenharmony_ci a, b = other.as_integer_ratio() 7917db96d56Sopenharmony_ci return timedelta(0, 0, _divide_and_round(usec * a, b)) 7927db96d56Sopenharmony_ci return NotImplemented 7937db96d56Sopenharmony_ci 7947db96d56Sopenharmony_ci __rmul__ = __mul__ 7957db96d56Sopenharmony_ci 7967db96d56Sopenharmony_ci def _to_microseconds(self): 7977db96d56Sopenharmony_ci return ((self._days * (24*3600) + self._seconds) * 1000000 + 7987db96d56Sopenharmony_ci self._microseconds) 7997db96d56Sopenharmony_ci 8007db96d56Sopenharmony_ci def __floordiv__(self, other): 8017db96d56Sopenharmony_ci if not isinstance(other, (int, timedelta)): 8027db96d56Sopenharmony_ci return NotImplemented 8037db96d56Sopenharmony_ci usec = self._to_microseconds() 8047db96d56Sopenharmony_ci if isinstance(other, timedelta): 8057db96d56Sopenharmony_ci return usec // other._to_microseconds() 8067db96d56Sopenharmony_ci if isinstance(other, int): 8077db96d56Sopenharmony_ci return timedelta(0, 0, usec // other) 8087db96d56Sopenharmony_ci 8097db96d56Sopenharmony_ci def __truediv__(self, other): 8107db96d56Sopenharmony_ci if not isinstance(other, (int, float, timedelta)): 8117db96d56Sopenharmony_ci return NotImplemented 8127db96d56Sopenharmony_ci usec = self._to_microseconds() 8137db96d56Sopenharmony_ci if isinstance(other, timedelta): 8147db96d56Sopenharmony_ci return usec / other._to_microseconds() 8157db96d56Sopenharmony_ci if isinstance(other, int): 8167db96d56Sopenharmony_ci return timedelta(0, 0, _divide_and_round(usec, other)) 8177db96d56Sopenharmony_ci if isinstance(other, float): 8187db96d56Sopenharmony_ci a, b = other.as_integer_ratio() 8197db96d56Sopenharmony_ci return timedelta(0, 0, _divide_and_round(b * usec, a)) 8207db96d56Sopenharmony_ci 8217db96d56Sopenharmony_ci def __mod__(self, other): 8227db96d56Sopenharmony_ci if isinstance(other, timedelta): 8237db96d56Sopenharmony_ci r = self._to_microseconds() % other._to_microseconds() 8247db96d56Sopenharmony_ci return timedelta(0, 0, r) 8257db96d56Sopenharmony_ci return NotImplemented 8267db96d56Sopenharmony_ci 8277db96d56Sopenharmony_ci def __divmod__(self, other): 8287db96d56Sopenharmony_ci if isinstance(other, timedelta): 8297db96d56Sopenharmony_ci q, r = divmod(self._to_microseconds(), 8307db96d56Sopenharmony_ci other._to_microseconds()) 8317db96d56Sopenharmony_ci return q, timedelta(0, 0, r) 8327db96d56Sopenharmony_ci return NotImplemented 8337db96d56Sopenharmony_ci 8347db96d56Sopenharmony_ci # Comparisons of timedelta objects with other. 8357db96d56Sopenharmony_ci 8367db96d56Sopenharmony_ci def __eq__(self, other): 8377db96d56Sopenharmony_ci if isinstance(other, timedelta): 8387db96d56Sopenharmony_ci return self._cmp(other) == 0 8397db96d56Sopenharmony_ci else: 8407db96d56Sopenharmony_ci return NotImplemented 8417db96d56Sopenharmony_ci 8427db96d56Sopenharmony_ci def __le__(self, other): 8437db96d56Sopenharmony_ci if isinstance(other, timedelta): 8447db96d56Sopenharmony_ci return self._cmp(other) <= 0 8457db96d56Sopenharmony_ci else: 8467db96d56Sopenharmony_ci return NotImplemented 8477db96d56Sopenharmony_ci 8487db96d56Sopenharmony_ci def __lt__(self, other): 8497db96d56Sopenharmony_ci if isinstance(other, timedelta): 8507db96d56Sopenharmony_ci return self._cmp(other) < 0 8517db96d56Sopenharmony_ci else: 8527db96d56Sopenharmony_ci return NotImplemented 8537db96d56Sopenharmony_ci 8547db96d56Sopenharmony_ci def __ge__(self, other): 8557db96d56Sopenharmony_ci if isinstance(other, timedelta): 8567db96d56Sopenharmony_ci return self._cmp(other) >= 0 8577db96d56Sopenharmony_ci else: 8587db96d56Sopenharmony_ci return NotImplemented 8597db96d56Sopenharmony_ci 8607db96d56Sopenharmony_ci def __gt__(self, other): 8617db96d56Sopenharmony_ci if isinstance(other, timedelta): 8627db96d56Sopenharmony_ci return self._cmp(other) > 0 8637db96d56Sopenharmony_ci else: 8647db96d56Sopenharmony_ci return NotImplemented 8657db96d56Sopenharmony_ci 8667db96d56Sopenharmony_ci def _cmp(self, other): 8677db96d56Sopenharmony_ci assert isinstance(other, timedelta) 8687db96d56Sopenharmony_ci return _cmp(self._getstate(), other._getstate()) 8697db96d56Sopenharmony_ci 8707db96d56Sopenharmony_ci def __hash__(self): 8717db96d56Sopenharmony_ci if self._hashcode == -1: 8727db96d56Sopenharmony_ci self._hashcode = hash(self._getstate()) 8737db96d56Sopenharmony_ci return self._hashcode 8747db96d56Sopenharmony_ci 8757db96d56Sopenharmony_ci def __bool__(self): 8767db96d56Sopenharmony_ci return (self._days != 0 or 8777db96d56Sopenharmony_ci self._seconds != 0 or 8787db96d56Sopenharmony_ci self._microseconds != 0) 8797db96d56Sopenharmony_ci 8807db96d56Sopenharmony_ci # Pickle support. 8817db96d56Sopenharmony_ci 8827db96d56Sopenharmony_ci def _getstate(self): 8837db96d56Sopenharmony_ci return (self._days, self._seconds, self._microseconds) 8847db96d56Sopenharmony_ci 8857db96d56Sopenharmony_ci def __reduce__(self): 8867db96d56Sopenharmony_ci return (self.__class__, self._getstate()) 8877db96d56Sopenharmony_ci 8887db96d56Sopenharmony_citimedelta.min = timedelta(-999999999) 8897db96d56Sopenharmony_citimedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, 8907db96d56Sopenharmony_ci microseconds=999999) 8917db96d56Sopenharmony_citimedelta.resolution = timedelta(microseconds=1) 8927db96d56Sopenharmony_ci 8937db96d56Sopenharmony_ciclass date: 8947db96d56Sopenharmony_ci """Concrete date type. 8957db96d56Sopenharmony_ci 8967db96d56Sopenharmony_ci Constructors: 8977db96d56Sopenharmony_ci 8987db96d56Sopenharmony_ci __new__() 8997db96d56Sopenharmony_ci fromtimestamp() 9007db96d56Sopenharmony_ci today() 9017db96d56Sopenharmony_ci fromordinal() 9027db96d56Sopenharmony_ci 9037db96d56Sopenharmony_ci Operators: 9047db96d56Sopenharmony_ci 9057db96d56Sopenharmony_ci __repr__, __str__ 9067db96d56Sopenharmony_ci __eq__, __le__, __lt__, __ge__, __gt__, __hash__ 9077db96d56Sopenharmony_ci __add__, __radd__, __sub__ (add/radd only with timedelta arg) 9087db96d56Sopenharmony_ci 9097db96d56Sopenharmony_ci Methods: 9107db96d56Sopenharmony_ci 9117db96d56Sopenharmony_ci timetuple() 9127db96d56Sopenharmony_ci toordinal() 9137db96d56Sopenharmony_ci weekday() 9147db96d56Sopenharmony_ci isoweekday(), isocalendar(), isoformat() 9157db96d56Sopenharmony_ci ctime() 9167db96d56Sopenharmony_ci strftime() 9177db96d56Sopenharmony_ci 9187db96d56Sopenharmony_ci Properties (readonly): 9197db96d56Sopenharmony_ci year, month, day 9207db96d56Sopenharmony_ci """ 9217db96d56Sopenharmony_ci __slots__ = '_year', '_month', '_day', '_hashcode' 9227db96d56Sopenharmony_ci 9237db96d56Sopenharmony_ci def __new__(cls, year, month=None, day=None): 9247db96d56Sopenharmony_ci """Constructor. 9257db96d56Sopenharmony_ci 9267db96d56Sopenharmony_ci Arguments: 9277db96d56Sopenharmony_ci 9287db96d56Sopenharmony_ci year, month, day (required, base 1) 9297db96d56Sopenharmony_ci """ 9307db96d56Sopenharmony_ci if (month is None and 9317db96d56Sopenharmony_ci isinstance(year, (bytes, str)) and len(year) == 4 and 9327db96d56Sopenharmony_ci 1 <= ord(year[2:3]) <= 12): 9337db96d56Sopenharmony_ci # Pickle support 9347db96d56Sopenharmony_ci if isinstance(year, str): 9357db96d56Sopenharmony_ci try: 9367db96d56Sopenharmony_ci year = year.encode('latin1') 9377db96d56Sopenharmony_ci except UnicodeEncodeError: 9387db96d56Sopenharmony_ci # More informative error message. 9397db96d56Sopenharmony_ci raise ValueError( 9407db96d56Sopenharmony_ci "Failed to encode latin1 string when unpickling " 9417db96d56Sopenharmony_ci "a date object. " 9427db96d56Sopenharmony_ci "pickle.load(data, encoding='latin1') is assumed.") 9437db96d56Sopenharmony_ci self = object.__new__(cls) 9447db96d56Sopenharmony_ci self.__setstate(year) 9457db96d56Sopenharmony_ci self._hashcode = -1 9467db96d56Sopenharmony_ci return self 9477db96d56Sopenharmony_ci year, month, day = _check_date_fields(year, month, day) 9487db96d56Sopenharmony_ci self = object.__new__(cls) 9497db96d56Sopenharmony_ci self._year = year 9507db96d56Sopenharmony_ci self._month = month 9517db96d56Sopenharmony_ci self._day = day 9527db96d56Sopenharmony_ci self._hashcode = -1 9537db96d56Sopenharmony_ci return self 9547db96d56Sopenharmony_ci 9557db96d56Sopenharmony_ci # Additional constructors 9567db96d56Sopenharmony_ci 9577db96d56Sopenharmony_ci @classmethod 9587db96d56Sopenharmony_ci def fromtimestamp(cls, t): 9597db96d56Sopenharmony_ci "Construct a date from a POSIX timestamp (like time.time())." 9607db96d56Sopenharmony_ci y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t) 9617db96d56Sopenharmony_ci return cls(y, m, d) 9627db96d56Sopenharmony_ci 9637db96d56Sopenharmony_ci @classmethod 9647db96d56Sopenharmony_ci def today(cls): 9657db96d56Sopenharmony_ci "Construct a date from time.time()." 9667db96d56Sopenharmony_ci t = _time.time() 9677db96d56Sopenharmony_ci return cls.fromtimestamp(t) 9687db96d56Sopenharmony_ci 9697db96d56Sopenharmony_ci @classmethod 9707db96d56Sopenharmony_ci def fromordinal(cls, n): 9717db96d56Sopenharmony_ci """Construct a date from a proleptic Gregorian ordinal. 9727db96d56Sopenharmony_ci 9737db96d56Sopenharmony_ci January 1 of year 1 is day 1. Only the year, month and day are 9747db96d56Sopenharmony_ci non-zero in the result. 9757db96d56Sopenharmony_ci """ 9767db96d56Sopenharmony_ci y, m, d = _ord2ymd(n) 9777db96d56Sopenharmony_ci return cls(y, m, d) 9787db96d56Sopenharmony_ci 9797db96d56Sopenharmony_ci @classmethod 9807db96d56Sopenharmony_ci def fromisoformat(cls, date_string): 9817db96d56Sopenharmony_ci """Construct a date from a string in ISO 8601 format.""" 9827db96d56Sopenharmony_ci if not isinstance(date_string, str): 9837db96d56Sopenharmony_ci raise TypeError('fromisoformat: argument must be str') 9847db96d56Sopenharmony_ci 9857db96d56Sopenharmony_ci if len(date_string) not in (7, 8, 10): 9867db96d56Sopenharmony_ci raise ValueError(f'Invalid isoformat string: {date_string!r}') 9877db96d56Sopenharmony_ci 9887db96d56Sopenharmony_ci try: 9897db96d56Sopenharmony_ci return cls(*_parse_isoformat_date(date_string)) 9907db96d56Sopenharmony_ci except Exception: 9917db96d56Sopenharmony_ci raise ValueError(f'Invalid isoformat string: {date_string!r}') 9927db96d56Sopenharmony_ci 9937db96d56Sopenharmony_ci @classmethod 9947db96d56Sopenharmony_ci def fromisocalendar(cls, year, week, day): 9957db96d56Sopenharmony_ci """Construct a date from the ISO year, week number and weekday. 9967db96d56Sopenharmony_ci 9977db96d56Sopenharmony_ci This is the inverse of the date.isocalendar() function""" 9987db96d56Sopenharmony_ci return cls(*_isoweek_to_gregorian(year, week, day)) 9997db96d56Sopenharmony_ci 10007db96d56Sopenharmony_ci # Conversions to string 10017db96d56Sopenharmony_ci 10027db96d56Sopenharmony_ci def __repr__(self): 10037db96d56Sopenharmony_ci """Convert to formal string, for repr(). 10047db96d56Sopenharmony_ci 10057db96d56Sopenharmony_ci >>> dt = datetime(2010, 1, 1) 10067db96d56Sopenharmony_ci >>> repr(dt) 10077db96d56Sopenharmony_ci 'datetime.datetime(2010, 1, 1, 0, 0)' 10087db96d56Sopenharmony_ci 10097db96d56Sopenharmony_ci >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc) 10107db96d56Sopenharmony_ci >>> repr(dt) 10117db96d56Sopenharmony_ci 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)' 10127db96d56Sopenharmony_ci """ 10137db96d56Sopenharmony_ci return "%s.%s(%d, %d, %d)" % (self.__class__.__module__, 10147db96d56Sopenharmony_ci self.__class__.__qualname__, 10157db96d56Sopenharmony_ci self._year, 10167db96d56Sopenharmony_ci self._month, 10177db96d56Sopenharmony_ci self._day) 10187db96d56Sopenharmony_ci # XXX These shouldn't depend on time.localtime(), because that 10197db96d56Sopenharmony_ci # clips the usable dates to [1970 .. 2038). At least ctime() is 10207db96d56Sopenharmony_ci # easily done without using strftime() -- that's better too because 10217db96d56Sopenharmony_ci # strftime("%c", ...) is locale specific. 10227db96d56Sopenharmony_ci 10237db96d56Sopenharmony_ci 10247db96d56Sopenharmony_ci def ctime(self): 10257db96d56Sopenharmony_ci "Return ctime() style string." 10267db96d56Sopenharmony_ci weekday = self.toordinal() % 7 or 7 10277db96d56Sopenharmony_ci return "%s %s %2d 00:00:00 %04d" % ( 10287db96d56Sopenharmony_ci _DAYNAMES[weekday], 10297db96d56Sopenharmony_ci _MONTHNAMES[self._month], 10307db96d56Sopenharmony_ci self._day, self._year) 10317db96d56Sopenharmony_ci 10327db96d56Sopenharmony_ci def strftime(self, fmt): 10337db96d56Sopenharmony_ci """ 10347db96d56Sopenharmony_ci Format using strftime(). 10357db96d56Sopenharmony_ci 10367db96d56Sopenharmony_ci Example: "%d/%m/%Y, %H:%M:%S" 10377db96d56Sopenharmony_ci """ 10387db96d56Sopenharmony_ci return _wrap_strftime(self, fmt, self.timetuple()) 10397db96d56Sopenharmony_ci 10407db96d56Sopenharmony_ci def __format__(self, fmt): 10417db96d56Sopenharmony_ci if not isinstance(fmt, str): 10427db96d56Sopenharmony_ci raise TypeError("must be str, not %s" % type(fmt).__name__) 10437db96d56Sopenharmony_ci if len(fmt) != 0: 10447db96d56Sopenharmony_ci return self.strftime(fmt) 10457db96d56Sopenharmony_ci return str(self) 10467db96d56Sopenharmony_ci 10477db96d56Sopenharmony_ci def isoformat(self): 10487db96d56Sopenharmony_ci """Return the date formatted according to ISO. 10497db96d56Sopenharmony_ci 10507db96d56Sopenharmony_ci This is 'YYYY-MM-DD'. 10517db96d56Sopenharmony_ci 10527db96d56Sopenharmony_ci References: 10537db96d56Sopenharmony_ci - http://www.w3.org/TR/NOTE-datetime 10547db96d56Sopenharmony_ci - http://www.cl.cam.ac.uk/~mgk25/iso-time.html 10557db96d56Sopenharmony_ci """ 10567db96d56Sopenharmony_ci return "%04d-%02d-%02d" % (self._year, self._month, self._day) 10577db96d56Sopenharmony_ci 10587db96d56Sopenharmony_ci __str__ = isoformat 10597db96d56Sopenharmony_ci 10607db96d56Sopenharmony_ci # Read-only field accessors 10617db96d56Sopenharmony_ci @property 10627db96d56Sopenharmony_ci def year(self): 10637db96d56Sopenharmony_ci """year (1-9999)""" 10647db96d56Sopenharmony_ci return self._year 10657db96d56Sopenharmony_ci 10667db96d56Sopenharmony_ci @property 10677db96d56Sopenharmony_ci def month(self): 10687db96d56Sopenharmony_ci """month (1-12)""" 10697db96d56Sopenharmony_ci return self._month 10707db96d56Sopenharmony_ci 10717db96d56Sopenharmony_ci @property 10727db96d56Sopenharmony_ci def day(self): 10737db96d56Sopenharmony_ci """day (1-31)""" 10747db96d56Sopenharmony_ci return self._day 10757db96d56Sopenharmony_ci 10767db96d56Sopenharmony_ci # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__, 10777db96d56Sopenharmony_ci # __hash__ (and helpers) 10787db96d56Sopenharmony_ci 10797db96d56Sopenharmony_ci def timetuple(self): 10807db96d56Sopenharmony_ci "Return local time tuple compatible with time.localtime()." 10817db96d56Sopenharmony_ci return _build_struct_time(self._year, self._month, self._day, 10827db96d56Sopenharmony_ci 0, 0, 0, -1) 10837db96d56Sopenharmony_ci 10847db96d56Sopenharmony_ci def toordinal(self): 10857db96d56Sopenharmony_ci """Return proleptic Gregorian ordinal for the year, month and day. 10867db96d56Sopenharmony_ci 10877db96d56Sopenharmony_ci January 1 of year 1 is day 1. Only the year, month and day values 10887db96d56Sopenharmony_ci contribute to the result. 10897db96d56Sopenharmony_ci """ 10907db96d56Sopenharmony_ci return _ymd2ord(self._year, self._month, self._day) 10917db96d56Sopenharmony_ci 10927db96d56Sopenharmony_ci def replace(self, year=None, month=None, day=None): 10937db96d56Sopenharmony_ci """Return a new date with new values for the specified fields.""" 10947db96d56Sopenharmony_ci if year is None: 10957db96d56Sopenharmony_ci year = self._year 10967db96d56Sopenharmony_ci if month is None: 10977db96d56Sopenharmony_ci month = self._month 10987db96d56Sopenharmony_ci if day is None: 10997db96d56Sopenharmony_ci day = self._day 11007db96d56Sopenharmony_ci return type(self)(year, month, day) 11017db96d56Sopenharmony_ci 11027db96d56Sopenharmony_ci # Comparisons of date objects with other. 11037db96d56Sopenharmony_ci 11047db96d56Sopenharmony_ci def __eq__(self, other): 11057db96d56Sopenharmony_ci if isinstance(other, date): 11067db96d56Sopenharmony_ci return self._cmp(other) == 0 11077db96d56Sopenharmony_ci return NotImplemented 11087db96d56Sopenharmony_ci 11097db96d56Sopenharmony_ci def __le__(self, other): 11107db96d56Sopenharmony_ci if isinstance(other, date): 11117db96d56Sopenharmony_ci return self._cmp(other) <= 0 11127db96d56Sopenharmony_ci return NotImplemented 11137db96d56Sopenharmony_ci 11147db96d56Sopenharmony_ci def __lt__(self, other): 11157db96d56Sopenharmony_ci if isinstance(other, date): 11167db96d56Sopenharmony_ci return self._cmp(other) < 0 11177db96d56Sopenharmony_ci return NotImplemented 11187db96d56Sopenharmony_ci 11197db96d56Sopenharmony_ci def __ge__(self, other): 11207db96d56Sopenharmony_ci if isinstance(other, date): 11217db96d56Sopenharmony_ci return self._cmp(other) >= 0 11227db96d56Sopenharmony_ci return NotImplemented 11237db96d56Sopenharmony_ci 11247db96d56Sopenharmony_ci def __gt__(self, other): 11257db96d56Sopenharmony_ci if isinstance(other, date): 11267db96d56Sopenharmony_ci return self._cmp(other) > 0 11277db96d56Sopenharmony_ci return NotImplemented 11287db96d56Sopenharmony_ci 11297db96d56Sopenharmony_ci def _cmp(self, other): 11307db96d56Sopenharmony_ci assert isinstance(other, date) 11317db96d56Sopenharmony_ci y, m, d = self._year, self._month, self._day 11327db96d56Sopenharmony_ci y2, m2, d2 = other._year, other._month, other._day 11337db96d56Sopenharmony_ci return _cmp((y, m, d), (y2, m2, d2)) 11347db96d56Sopenharmony_ci 11357db96d56Sopenharmony_ci def __hash__(self): 11367db96d56Sopenharmony_ci "Hash." 11377db96d56Sopenharmony_ci if self._hashcode == -1: 11387db96d56Sopenharmony_ci self._hashcode = hash(self._getstate()) 11397db96d56Sopenharmony_ci return self._hashcode 11407db96d56Sopenharmony_ci 11417db96d56Sopenharmony_ci # Computations 11427db96d56Sopenharmony_ci 11437db96d56Sopenharmony_ci def __add__(self, other): 11447db96d56Sopenharmony_ci "Add a date to a timedelta." 11457db96d56Sopenharmony_ci if isinstance(other, timedelta): 11467db96d56Sopenharmony_ci o = self.toordinal() + other.days 11477db96d56Sopenharmony_ci if 0 < o <= _MAXORDINAL: 11487db96d56Sopenharmony_ci return type(self).fromordinal(o) 11497db96d56Sopenharmony_ci raise OverflowError("result out of range") 11507db96d56Sopenharmony_ci return NotImplemented 11517db96d56Sopenharmony_ci 11527db96d56Sopenharmony_ci __radd__ = __add__ 11537db96d56Sopenharmony_ci 11547db96d56Sopenharmony_ci def __sub__(self, other): 11557db96d56Sopenharmony_ci """Subtract two dates, or a date and a timedelta.""" 11567db96d56Sopenharmony_ci if isinstance(other, timedelta): 11577db96d56Sopenharmony_ci return self + timedelta(-other.days) 11587db96d56Sopenharmony_ci if isinstance(other, date): 11597db96d56Sopenharmony_ci days1 = self.toordinal() 11607db96d56Sopenharmony_ci days2 = other.toordinal() 11617db96d56Sopenharmony_ci return timedelta(days1 - days2) 11627db96d56Sopenharmony_ci return NotImplemented 11637db96d56Sopenharmony_ci 11647db96d56Sopenharmony_ci def weekday(self): 11657db96d56Sopenharmony_ci "Return day of the week, where Monday == 0 ... Sunday == 6." 11667db96d56Sopenharmony_ci return (self.toordinal() + 6) % 7 11677db96d56Sopenharmony_ci 11687db96d56Sopenharmony_ci # Day-of-the-week and week-of-the-year, according to ISO 11697db96d56Sopenharmony_ci 11707db96d56Sopenharmony_ci def isoweekday(self): 11717db96d56Sopenharmony_ci "Return day of the week, where Monday == 1 ... Sunday == 7." 11727db96d56Sopenharmony_ci # 1-Jan-0001 is a Monday 11737db96d56Sopenharmony_ci return self.toordinal() % 7 or 7 11747db96d56Sopenharmony_ci 11757db96d56Sopenharmony_ci def isocalendar(self): 11767db96d56Sopenharmony_ci """Return a named tuple containing ISO year, week number, and weekday. 11777db96d56Sopenharmony_ci 11787db96d56Sopenharmony_ci The first ISO week of the year is the (Mon-Sun) week 11797db96d56Sopenharmony_ci containing the year's first Thursday; everything else derives 11807db96d56Sopenharmony_ci from that. 11817db96d56Sopenharmony_ci 11827db96d56Sopenharmony_ci The first week is 1; Monday is 1 ... Sunday is 7. 11837db96d56Sopenharmony_ci 11847db96d56Sopenharmony_ci ISO calendar algorithm taken from 11857db96d56Sopenharmony_ci http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm 11867db96d56Sopenharmony_ci (used with permission) 11877db96d56Sopenharmony_ci """ 11887db96d56Sopenharmony_ci year = self._year 11897db96d56Sopenharmony_ci week1monday = _isoweek1monday(year) 11907db96d56Sopenharmony_ci today = _ymd2ord(self._year, self._month, self._day) 11917db96d56Sopenharmony_ci # Internally, week and day have origin 0 11927db96d56Sopenharmony_ci week, day = divmod(today - week1monday, 7) 11937db96d56Sopenharmony_ci if week < 0: 11947db96d56Sopenharmony_ci year -= 1 11957db96d56Sopenharmony_ci week1monday = _isoweek1monday(year) 11967db96d56Sopenharmony_ci week, day = divmod(today - week1monday, 7) 11977db96d56Sopenharmony_ci elif week >= 52: 11987db96d56Sopenharmony_ci if today >= _isoweek1monday(year+1): 11997db96d56Sopenharmony_ci year += 1 12007db96d56Sopenharmony_ci week = 0 12017db96d56Sopenharmony_ci return _IsoCalendarDate(year, week+1, day+1) 12027db96d56Sopenharmony_ci 12037db96d56Sopenharmony_ci # Pickle support. 12047db96d56Sopenharmony_ci 12057db96d56Sopenharmony_ci def _getstate(self): 12067db96d56Sopenharmony_ci yhi, ylo = divmod(self._year, 256) 12077db96d56Sopenharmony_ci return bytes([yhi, ylo, self._month, self._day]), 12087db96d56Sopenharmony_ci 12097db96d56Sopenharmony_ci def __setstate(self, string): 12107db96d56Sopenharmony_ci yhi, ylo, self._month, self._day = string 12117db96d56Sopenharmony_ci self._year = yhi * 256 + ylo 12127db96d56Sopenharmony_ci 12137db96d56Sopenharmony_ci def __reduce__(self): 12147db96d56Sopenharmony_ci return (self.__class__, self._getstate()) 12157db96d56Sopenharmony_ci 12167db96d56Sopenharmony_ci_date_class = date # so functions w/ args named "date" can get at the class 12177db96d56Sopenharmony_ci 12187db96d56Sopenharmony_cidate.min = date(1, 1, 1) 12197db96d56Sopenharmony_cidate.max = date(9999, 12, 31) 12207db96d56Sopenharmony_cidate.resolution = timedelta(days=1) 12217db96d56Sopenharmony_ci 12227db96d56Sopenharmony_ci 12237db96d56Sopenharmony_ciclass tzinfo: 12247db96d56Sopenharmony_ci """Abstract base class for time zone info classes. 12257db96d56Sopenharmony_ci 12267db96d56Sopenharmony_ci Subclasses must override the name(), utcoffset() and dst() methods. 12277db96d56Sopenharmony_ci """ 12287db96d56Sopenharmony_ci __slots__ = () 12297db96d56Sopenharmony_ci 12307db96d56Sopenharmony_ci def tzname(self, dt): 12317db96d56Sopenharmony_ci "datetime -> string name of time zone." 12327db96d56Sopenharmony_ci raise NotImplementedError("tzinfo subclass must override tzname()") 12337db96d56Sopenharmony_ci 12347db96d56Sopenharmony_ci def utcoffset(self, dt): 12357db96d56Sopenharmony_ci "datetime -> timedelta, positive for east of UTC, negative for west of UTC" 12367db96d56Sopenharmony_ci raise NotImplementedError("tzinfo subclass must override utcoffset()") 12377db96d56Sopenharmony_ci 12387db96d56Sopenharmony_ci def dst(self, dt): 12397db96d56Sopenharmony_ci """datetime -> DST offset as timedelta, positive for east of UTC. 12407db96d56Sopenharmony_ci 12417db96d56Sopenharmony_ci Return 0 if DST not in effect. utcoffset() must include the DST 12427db96d56Sopenharmony_ci offset. 12437db96d56Sopenharmony_ci """ 12447db96d56Sopenharmony_ci raise NotImplementedError("tzinfo subclass must override dst()") 12457db96d56Sopenharmony_ci 12467db96d56Sopenharmony_ci def fromutc(self, dt): 12477db96d56Sopenharmony_ci "datetime in UTC -> datetime in local time." 12487db96d56Sopenharmony_ci 12497db96d56Sopenharmony_ci if not isinstance(dt, datetime): 12507db96d56Sopenharmony_ci raise TypeError("fromutc() requires a datetime argument") 12517db96d56Sopenharmony_ci if dt.tzinfo is not self: 12527db96d56Sopenharmony_ci raise ValueError("dt.tzinfo is not self") 12537db96d56Sopenharmony_ci 12547db96d56Sopenharmony_ci dtoff = dt.utcoffset() 12557db96d56Sopenharmony_ci if dtoff is None: 12567db96d56Sopenharmony_ci raise ValueError("fromutc() requires a non-None utcoffset() " 12577db96d56Sopenharmony_ci "result") 12587db96d56Sopenharmony_ci 12597db96d56Sopenharmony_ci # See the long comment block at the end of this file for an 12607db96d56Sopenharmony_ci # explanation of this algorithm. 12617db96d56Sopenharmony_ci dtdst = dt.dst() 12627db96d56Sopenharmony_ci if dtdst is None: 12637db96d56Sopenharmony_ci raise ValueError("fromutc() requires a non-None dst() result") 12647db96d56Sopenharmony_ci delta = dtoff - dtdst 12657db96d56Sopenharmony_ci if delta: 12667db96d56Sopenharmony_ci dt += delta 12677db96d56Sopenharmony_ci dtdst = dt.dst() 12687db96d56Sopenharmony_ci if dtdst is None: 12697db96d56Sopenharmony_ci raise ValueError("fromutc(): dt.dst gave inconsistent " 12707db96d56Sopenharmony_ci "results; cannot convert") 12717db96d56Sopenharmony_ci return dt + dtdst 12727db96d56Sopenharmony_ci 12737db96d56Sopenharmony_ci # Pickle support. 12747db96d56Sopenharmony_ci 12757db96d56Sopenharmony_ci def __reduce__(self): 12767db96d56Sopenharmony_ci getinitargs = getattr(self, "__getinitargs__", None) 12777db96d56Sopenharmony_ci if getinitargs: 12787db96d56Sopenharmony_ci args = getinitargs() 12797db96d56Sopenharmony_ci else: 12807db96d56Sopenharmony_ci args = () 12817db96d56Sopenharmony_ci return (self.__class__, args, self.__getstate__()) 12827db96d56Sopenharmony_ci 12837db96d56Sopenharmony_ci 12847db96d56Sopenharmony_ciclass IsoCalendarDate(tuple): 12857db96d56Sopenharmony_ci 12867db96d56Sopenharmony_ci def __new__(cls, year, week, weekday, /): 12877db96d56Sopenharmony_ci return super().__new__(cls, (year, week, weekday)) 12887db96d56Sopenharmony_ci 12897db96d56Sopenharmony_ci @property 12907db96d56Sopenharmony_ci def year(self): 12917db96d56Sopenharmony_ci return self[0] 12927db96d56Sopenharmony_ci 12937db96d56Sopenharmony_ci @property 12947db96d56Sopenharmony_ci def week(self): 12957db96d56Sopenharmony_ci return self[1] 12967db96d56Sopenharmony_ci 12977db96d56Sopenharmony_ci @property 12987db96d56Sopenharmony_ci def weekday(self): 12997db96d56Sopenharmony_ci return self[2] 13007db96d56Sopenharmony_ci 13017db96d56Sopenharmony_ci def __reduce__(self): 13027db96d56Sopenharmony_ci # This code is intended to pickle the object without making the 13037db96d56Sopenharmony_ci # class public. See https://bugs.python.org/msg352381 13047db96d56Sopenharmony_ci return (tuple, (tuple(self),)) 13057db96d56Sopenharmony_ci 13067db96d56Sopenharmony_ci def __repr__(self): 13077db96d56Sopenharmony_ci return (f'{self.__class__.__name__}' 13087db96d56Sopenharmony_ci f'(year={self[0]}, week={self[1]}, weekday={self[2]})') 13097db96d56Sopenharmony_ci 13107db96d56Sopenharmony_ci 13117db96d56Sopenharmony_ci_IsoCalendarDate = IsoCalendarDate 13127db96d56Sopenharmony_cidel IsoCalendarDate 13137db96d56Sopenharmony_ci_tzinfo_class = tzinfo 13147db96d56Sopenharmony_ci 13157db96d56Sopenharmony_ciclass time: 13167db96d56Sopenharmony_ci """Time with time zone. 13177db96d56Sopenharmony_ci 13187db96d56Sopenharmony_ci Constructors: 13197db96d56Sopenharmony_ci 13207db96d56Sopenharmony_ci __new__() 13217db96d56Sopenharmony_ci 13227db96d56Sopenharmony_ci Operators: 13237db96d56Sopenharmony_ci 13247db96d56Sopenharmony_ci __repr__, __str__ 13257db96d56Sopenharmony_ci __eq__, __le__, __lt__, __ge__, __gt__, __hash__ 13267db96d56Sopenharmony_ci 13277db96d56Sopenharmony_ci Methods: 13287db96d56Sopenharmony_ci 13297db96d56Sopenharmony_ci strftime() 13307db96d56Sopenharmony_ci isoformat() 13317db96d56Sopenharmony_ci utcoffset() 13327db96d56Sopenharmony_ci tzname() 13337db96d56Sopenharmony_ci dst() 13347db96d56Sopenharmony_ci 13357db96d56Sopenharmony_ci Properties (readonly): 13367db96d56Sopenharmony_ci hour, minute, second, microsecond, tzinfo, fold 13377db96d56Sopenharmony_ci """ 13387db96d56Sopenharmony_ci __slots__ = '_hour', '_minute', '_second', '_microsecond', '_tzinfo', '_hashcode', '_fold' 13397db96d56Sopenharmony_ci 13407db96d56Sopenharmony_ci def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0): 13417db96d56Sopenharmony_ci """Constructor. 13427db96d56Sopenharmony_ci 13437db96d56Sopenharmony_ci Arguments: 13447db96d56Sopenharmony_ci 13457db96d56Sopenharmony_ci hour, minute (required) 13467db96d56Sopenharmony_ci second, microsecond (default to zero) 13477db96d56Sopenharmony_ci tzinfo (default to None) 13487db96d56Sopenharmony_ci fold (keyword only, default to zero) 13497db96d56Sopenharmony_ci """ 13507db96d56Sopenharmony_ci if (isinstance(hour, (bytes, str)) and len(hour) == 6 and 13517db96d56Sopenharmony_ci ord(hour[0:1])&0x7F < 24): 13527db96d56Sopenharmony_ci # Pickle support 13537db96d56Sopenharmony_ci if isinstance(hour, str): 13547db96d56Sopenharmony_ci try: 13557db96d56Sopenharmony_ci hour = hour.encode('latin1') 13567db96d56Sopenharmony_ci except UnicodeEncodeError: 13577db96d56Sopenharmony_ci # More informative error message. 13587db96d56Sopenharmony_ci raise ValueError( 13597db96d56Sopenharmony_ci "Failed to encode latin1 string when unpickling " 13607db96d56Sopenharmony_ci "a time object. " 13617db96d56Sopenharmony_ci "pickle.load(data, encoding='latin1') is assumed.") 13627db96d56Sopenharmony_ci self = object.__new__(cls) 13637db96d56Sopenharmony_ci self.__setstate(hour, minute or None) 13647db96d56Sopenharmony_ci self._hashcode = -1 13657db96d56Sopenharmony_ci return self 13667db96d56Sopenharmony_ci hour, minute, second, microsecond, fold = _check_time_fields( 13677db96d56Sopenharmony_ci hour, minute, second, microsecond, fold) 13687db96d56Sopenharmony_ci _check_tzinfo_arg(tzinfo) 13697db96d56Sopenharmony_ci self = object.__new__(cls) 13707db96d56Sopenharmony_ci self._hour = hour 13717db96d56Sopenharmony_ci self._minute = minute 13727db96d56Sopenharmony_ci self._second = second 13737db96d56Sopenharmony_ci self._microsecond = microsecond 13747db96d56Sopenharmony_ci self._tzinfo = tzinfo 13757db96d56Sopenharmony_ci self._hashcode = -1 13767db96d56Sopenharmony_ci self._fold = fold 13777db96d56Sopenharmony_ci return self 13787db96d56Sopenharmony_ci 13797db96d56Sopenharmony_ci # Read-only field accessors 13807db96d56Sopenharmony_ci @property 13817db96d56Sopenharmony_ci def hour(self): 13827db96d56Sopenharmony_ci """hour (0-23)""" 13837db96d56Sopenharmony_ci return self._hour 13847db96d56Sopenharmony_ci 13857db96d56Sopenharmony_ci @property 13867db96d56Sopenharmony_ci def minute(self): 13877db96d56Sopenharmony_ci """minute (0-59)""" 13887db96d56Sopenharmony_ci return self._minute 13897db96d56Sopenharmony_ci 13907db96d56Sopenharmony_ci @property 13917db96d56Sopenharmony_ci def second(self): 13927db96d56Sopenharmony_ci """second (0-59)""" 13937db96d56Sopenharmony_ci return self._second 13947db96d56Sopenharmony_ci 13957db96d56Sopenharmony_ci @property 13967db96d56Sopenharmony_ci def microsecond(self): 13977db96d56Sopenharmony_ci """microsecond (0-999999)""" 13987db96d56Sopenharmony_ci return self._microsecond 13997db96d56Sopenharmony_ci 14007db96d56Sopenharmony_ci @property 14017db96d56Sopenharmony_ci def tzinfo(self): 14027db96d56Sopenharmony_ci """timezone info object""" 14037db96d56Sopenharmony_ci return self._tzinfo 14047db96d56Sopenharmony_ci 14057db96d56Sopenharmony_ci @property 14067db96d56Sopenharmony_ci def fold(self): 14077db96d56Sopenharmony_ci return self._fold 14087db96d56Sopenharmony_ci 14097db96d56Sopenharmony_ci # Standard conversions, __hash__ (and helpers) 14107db96d56Sopenharmony_ci 14117db96d56Sopenharmony_ci # Comparisons of time objects with other. 14127db96d56Sopenharmony_ci 14137db96d56Sopenharmony_ci def __eq__(self, other): 14147db96d56Sopenharmony_ci if isinstance(other, time): 14157db96d56Sopenharmony_ci return self._cmp(other, allow_mixed=True) == 0 14167db96d56Sopenharmony_ci else: 14177db96d56Sopenharmony_ci return NotImplemented 14187db96d56Sopenharmony_ci 14197db96d56Sopenharmony_ci def __le__(self, other): 14207db96d56Sopenharmony_ci if isinstance(other, time): 14217db96d56Sopenharmony_ci return self._cmp(other) <= 0 14227db96d56Sopenharmony_ci else: 14237db96d56Sopenharmony_ci return NotImplemented 14247db96d56Sopenharmony_ci 14257db96d56Sopenharmony_ci def __lt__(self, other): 14267db96d56Sopenharmony_ci if isinstance(other, time): 14277db96d56Sopenharmony_ci return self._cmp(other) < 0 14287db96d56Sopenharmony_ci else: 14297db96d56Sopenharmony_ci return NotImplemented 14307db96d56Sopenharmony_ci 14317db96d56Sopenharmony_ci def __ge__(self, other): 14327db96d56Sopenharmony_ci if isinstance(other, time): 14337db96d56Sopenharmony_ci return self._cmp(other) >= 0 14347db96d56Sopenharmony_ci else: 14357db96d56Sopenharmony_ci return NotImplemented 14367db96d56Sopenharmony_ci 14377db96d56Sopenharmony_ci def __gt__(self, other): 14387db96d56Sopenharmony_ci if isinstance(other, time): 14397db96d56Sopenharmony_ci return self._cmp(other) > 0 14407db96d56Sopenharmony_ci else: 14417db96d56Sopenharmony_ci return NotImplemented 14427db96d56Sopenharmony_ci 14437db96d56Sopenharmony_ci def _cmp(self, other, allow_mixed=False): 14447db96d56Sopenharmony_ci assert isinstance(other, time) 14457db96d56Sopenharmony_ci mytz = self._tzinfo 14467db96d56Sopenharmony_ci ottz = other._tzinfo 14477db96d56Sopenharmony_ci myoff = otoff = None 14487db96d56Sopenharmony_ci 14497db96d56Sopenharmony_ci if mytz is ottz: 14507db96d56Sopenharmony_ci base_compare = True 14517db96d56Sopenharmony_ci else: 14527db96d56Sopenharmony_ci myoff = self.utcoffset() 14537db96d56Sopenharmony_ci otoff = other.utcoffset() 14547db96d56Sopenharmony_ci base_compare = myoff == otoff 14557db96d56Sopenharmony_ci 14567db96d56Sopenharmony_ci if base_compare: 14577db96d56Sopenharmony_ci return _cmp((self._hour, self._minute, self._second, 14587db96d56Sopenharmony_ci self._microsecond), 14597db96d56Sopenharmony_ci (other._hour, other._minute, other._second, 14607db96d56Sopenharmony_ci other._microsecond)) 14617db96d56Sopenharmony_ci if myoff is None or otoff is None: 14627db96d56Sopenharmony_ci if allow_mixed: 14637db96d56Sopenharmony_ci return 2 # arbitrary non-zero value 14647db96d56Sopenharmony_ci else: 14657db96d56Sopenharmony_ci raise TypeError("cannot compare naive and aware times") 14667db96d56Sopenharmony_ci myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1) 14677db96d56Sopenharmony_ci othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1) 14687db96d56Sopenharmony_ci return _cmp((myhhmm, self._second, self._microsecond), 14697db96d56Sopenharmony_ci (othhmm, other._second, other._microsecond)) 14707db96d56Sopenharmony_ci 14717db96d56Sopenharmony_ci def __hash__(self): 14727db96d56Sopenharmony_ci """Hash.""" 14737db96d56Sopenharmony_ci if self._hashcode == -1: 14747db96d56Sopenharmony_ci if self.fold: 14757db96d56Sopenharmony_ci t = self.replace(fold=0) 14767db96d56Sopenharmony_ci else: 14777db96d56Sopenharmony_ci t = self 14787db96d56Sopenharmony_ci tzoff = t.utcoffset() 14797db96d56Sopenharmony_ci if not tzoff: # zero or None 14807db96d56Sopenharmony_ci self._hashcode = hash(t._getstate()[0]) 14817db96d56Sopenharmony_ci else: 14827db96d56Sopenharmony_ci h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff, 14837db96d56Sopenharmony_ci timedelta(hours=1)) 14847db96d56Sopenharmony_ci assert not m % timedelta(minutes=1), "whole minute" 14857db96d56Sopenharmony_ci m //= timedelta(minutes=1) 14867db96d56Sopenharmony_ci if 0 <= h < 24: 14877db96d56Sopenharmony_ci self._hashcode = hash(time(h, m, self.second, self.microsecond)) 14887db96d56Sopenharmony_ci else: 14897db96d56Sopenharmony_ci self._hashcode = hash((h, m, self.second, self.microsecond)) 14907db96d56Sopenharmony_ci return self._hashcode 14917db96d56Sopenharmony_ci 14927db96d56Sopenharmony_ci # Conversion to string 14937db96d56Sopenharmony_ci 14947db96d56Sopenharmony_ci def _tzstr(self): 14957db96d56Sopenharmony_ci """Return formatted timezone offset (+xx:xx) or an empty string.""" 14967db96d56Sopenharmony_ci off = self.utcoffset() 14977db96d56Sopenharmony_ci return _format_offset(off) 14987db96d56Sopenharmony_ci 14997db96d56Sopenharmony_ci def __repr__(self): 15007db96d56Sopenharmony_ci """Convert to formal string, for repr().""" 15017db96d56Sopenharmony_ci if self._microsecond != 0: 15027db96d56Sopenharmony_ci s = ", %d, %d" % (self._second, self._microsecond) 15037db96d56Sopenharmony_ci elif self._second != 0: 15047db96d56Sopenharmony_ci s = ", %d" % self._second 15057db96d56Sopenharmony_ci else: 15067db96d56Sopenharmony_ci s = "" 15077db96d56Sopenharmony_ci s= "%s.%s(%d, %d%s)" % (self.__class__.__module__, 15087db96d56Sopenharmony_ci self.__class__.__qualname__, 15097db96d56Sopenharmony_ci self._hour, self._minute, s) 15107db96d56Sopenharmony_ci if self._tzinfo is not None: 15117db96d56Sopenharmony_ci assert s[-1:] == ")" 15127db96d56Sopenharmony_ci s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" 15137db96d56Sopenharmony_ci if self._fold: 15147db96d56Sopenharmony_ci assert s[-1:] == ")" 15157db96d56Sopenharmony_ci s = s[:-1] + ", fold=1)" 15167db96d56Sopenharmony_ci return s 15177db96d56Sopenharmony_ci 15187db96d56Sopenharmony_ci def isoformat(self, timespec='auto'): 15197db96d56Sopenharmony_ci """Return the time formatted according to ISO. 15207db96d56Sopenharmony_ci 15217db96d56Sopenharmony_ci The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional 15227db96d56Sopenharmony_ci part is omitted if self.microsecond == 0. 15237db96d56Sopenharmony_ci 15247db96d56Sopenharmony_ci The optional argument timespec specifies the number of additional 15257db96d56Sopenharmony_ci terms of the time to include. Valid options are 'auto', 'hours', 15267db96d56Sopenharmony_ci 'minutes', 'seconds', 'milliseconds' and 'microseconds'. 15277db96d56Sopenharmony_ci """ 15287db96d56Sopenharmony_ci s = _format_time(self._hour, self._minute, self._second, 15297db96d56Sopenharmony_ci self._microsecond, timespec) 15307db96d56Sopenharmony_ci tz = self._tzstr() 15317db96d56Sopenharmony_ci if tz: 15327db96d56Sopenharmony_ci s += tz 15337db96d56Sopenharmony_ci return s 15347db96d56Sopenharmony_ci 15357db96d56Sopenharmony_ci __str__ = isoformat 15367db96d56Sopenharmony_ci 15377db96d56Sopenharmony_ci @classmethod 15387db96d56Sopenharmony_ci def fromisoformat(cls, time_string): 15397db96d56Sopenharmony_ci """Construct a time from a string in one of the ISO 8601 formats.""" 15407db96d56Sopenharmony_ci if not isinstance(time_string, str): 15417db96d56Sopenharmony_ci raise TypeError('fromisoformat: argument must be str') 15427db96d56Sopenharmony_ci 15437db96d56Sopenharmony_ci # The spec actually requires that time-only ISO 8601 strings start with 15447db96d56Sopenharmony_ci # T, but the extended format allows this to be omitted as long as there 15457db96d56Sopenharmony_ci # is no ambiguity with date strings. 15467db96d56Sopenharmony_ci time_string = time_string.removeprefix('T') 15477db96d56Sopenharmony_ci 15487db96d56Sopenharmony_ci try: 15497db96d56Sopenharmony_ci return cls(*_parse_isoformat_time(time_string)) 15507db96d56Sopenharmony_ci except Exception: 15517db96d56Sopenharmony_ci raise ValueError(f'Invalid isoformat string: {time_string!r}') 15527db96d56Sopenharmony_ci 15537db96d56Sopenharmony_ci 15547db96d56Sopenharmony_ci def strftime(self, fmt): 15557db96d56Sopenharmony_ci """Format using strftime(). The date part of the timestamp passed 15567db96d56Sopenharmony_ci to underlying strftime should not be used. 15577db96d56Sopenharmony_ci """ 15587db96d56Sopenharmony_ci # The year must be >= 1000 else Python's strftime implementation 15597db96d56Sopenharmony_ci # can raise a bogus exception. 15607db96d56Sopenharmony_ci timetuple = (1900, 1, 1, 15617db96d56Sopenharmony_ci self._hour, self._minute, self._second, 15627db96d56Sopenharmony_ci 0, 1, -1) 15637db96d56Sopenharmony_ci return _wrap_strftime(self, fmt, timetuple) 15647db96d56Sopenharmony_ci 15657db96d56Sopenharmony_ci def __format__(self, fmt): 15667db96d56Sopenharmony_ci if not isinstance(fmt, str): 15677db96d56Sopenharmony_ci raise TypeError("must be str, not %s" % type(fmt).__name__) 15687db96d56Sopenharmony_ci if len(fmt) != 0: 15697db96d56Sopenharmony_ci return self.strftime(fmt) 15707db96d56Sopenharmony_ci return str(self) 15717db96d56Sopenharmony_ci 15727db96d56Sopenharmony_ci # Timezone functions 15737db96d56Sopenharmony_ci 15747db96d56Sopenharmony_ci def utcoffset(self): 15757db96d56Sopenharmony_ci """Return the timezone offset as timedelta, positive east of UTC 15767db96d56Sopenharmony_ci (negative west of UTC).""" 15777db96d56Sopenharmony_ci if self._tzinfo is None: 15787db96d56Sopenharmony_ci return None 15797db96d56Sopenharmony_ci offset = self._tzinfo.utcoffset(None) 15807db96d56Sopenharmony_ci _check_utc_offset("utcoffset", offset) 15817db96d56Sopenharmony_ci return offset 15827db96d56Sopenharmony_ci 15837db96d56Sopenharmony_ci def tzname(self): 15847db96d56Sopenharmony_ci """Return the timezone name. 15857db96d56Sopenharmony_ci 15867db96d56Sopenharmony_ci Note that the name is 100% informational -- there's no requirement that 15877db96d56Sopenharmony_ci it mean anything in particular. For example, "GMT", "UTC", "-500", 15887db96d56Sopenharmony_ci "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. 15897db96d56Sopenharmony_ci """ 15907db96d56Sopenharmony_ci if self._tzinfo is None: 15917db96d56Sopenharmony_ci return None 15927db96d56Sopenharmony_ci name = self._tzinfo.tzname(None) 15937db96d56Sopenharmony_ci _check_tzname(name) 15947db96d56Sopenharmony_ci return name 15957db96d56Sopenharmony_ci 15967db96d56Sopenharmony_ci def dst(self): 15977db96d56Sopenharmony_ci """Return 0 if DST is not in effect, or the DST offset (as timedelta 15987db96d56Sopenharmony_ci positive eastward) if DST is in effect. 15997db96d56Sopenharmony_ci 16007db96d56Sopenharmony_ci This is purely informational; the DST offset has already been added to 16017db96d56Sopenharmony_ci the UTC offset returned by utcoffset() if applicable, so there's no 16027db96d56Sopenharmony_ci need to consult dst() unless you're interested in displaying the DST 16037db96d56Sopenharmony_ci info. 16047db96d56Sopenharmony_ci """ 16057db96d56Sopenharmony_ci if self._tzinfo is None: 16067db96d56Sopenharmony_ci return None 16077db96d56Sopenharmony_ci offset = self._tzinfo.dst(None) 16087db96d56Sopenharmony_ci _check_utc_offset("dst", offset) 16097db96d56Sopenharmony_ci return offset 16107db96d56Sopenharmony_ci 16117db96d56Sopenharmony_ci def replace(self, hour=None, minute=None, second=None, microsecond=None, 16127db96d56Sopenharmony_ci tzinfo=True, *, fold=None): 16137db96d56Sopenharmony_ci """Return a new time with new values for the specified fields.""" 16147db96d56Sopenharmony_ci if hour is None: 16157db96d56Sopenharmony_ci hour = self.hour 16167db96d56Sopenharmony_ci if minute is None: 16177db96d56Sopenharmony_ci minute = self.minute 16187db96d56Sopenharmony_ci if second is None: 16197db96d56Sopenharmony_ci second = self.second 16207db96d56Sopenharmony_ci if microsecond is None: 16217db96d56Sopenharmony_ci microsecond = self.microsecond 16227db96d56Sopenharmony_ci if tzinfo is True: 16237db96d56Sopenharmony_ci tzinfo = self.tzinfo 16247db96d56Sopenharmony_ci if fold is None: 16257db96d56Sopenharmony_ci fold = self._fold 16267db96d56Sopenharmony_ci return type(self)(hour, minute, second, microsecond, tzinfo, fold=fold) 16277db96d56Sopenharmony_ci 16287db96d56Sopenharmony_ci # Pickle support. 16297db96d56Sopenharmony_ci 16307db96d56Sopenharmony_ci def _getstate(self, protocol=3): 16317db96d56Sopenharmony_ci us2, us3 = divmod(self._microsecond, 256) 16327db96d56Sopenharmony_ci us1, us2 = divmod(us2, 256) 16337db96d56Sopenharmony_ci h = self._hour 16347db96d56Sopenharmony_ci if self._fold and protocol > 3: 16357db96d56Sopenharmony_ci h += 128 16367db96d56Sopenharmony_ci basestate = bytes([h, self._minute, self._second, 16377db96d56Sopenharmony_ci us1, us2, us3]) 16387db96d56Sopenharmony_ci if self._tzinfo is None: 16397db96d56Sopenharmony_ci return (basestate,) 16407db96d56Sopenharmony_ci else: 16417db96d56Sopenharmony_ci return (basestate, self._tzinfo) 16427db96d56Sopenharmony_ci 16437db96d56Sopenharmony_ci def __setstate(self, string, tzinfo): 16447db96d56Sopenharmony_ci if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class): 16457db96d56Sopenharmony_ci raise TypeError("bad tzinfo state arg") 16467db96d56Sopenharmony_ci h, self._minute, self._second, us1, us2, us3 = string 16477db96d56Sopenharmony_ci if h > 127: 16487db96d56Sopenharmony_ci self._fold = 1 16497db96d56Sopenharmony_ci self._hour = h - 128 16507db96d56Sopenharmony_ci else: 16517db96d56Sopenharmony_ci self._fold = 0 16527db96d56Sopenharmony_ci self._hour = h 16537db96d56Sopenharmony_ci self._microsecond = (((us1 << 8) | us2) << 8) | us3 16547db96d56Sopenharmony_ci self._tzinfo = tzinfo 16557db96d56Sopenharmony_ci 16567db96d56Sopenharmony_ci def __reduce_ex__(self, protocol): 16577db96d56Sopenharmony_ci return (self.__class__, self._getstate(protocol)) 16587db96d56Sopenharmony_ci 16597db96d56Sopenharmony_ci def __reduce__(self): 16607db96d56Sopenharmony_ci return self.__reduce_ex__(2) 16617db96d56Sopenharmony_ci 16627db96d56Sopenharmony_ci_time_class = time # so functions w/ args named "time" can get at the class 16637db96d56Sopenharmony_ci 16647db96d56Sopenharmony_citime.min = time(0, 0, 0) 16657db96d56Sopenharmony_citime.max = time(23, 59, 59, 999999) 16667db96d56Sopenharmony_citime.resolution = timedelta(microseconds=1) 16677db96d56Sopenharmony_ci 16687db96d56Sopenharmony_ci 16697db96d56Sopenharmony_ciclass datetime(date): 16707db96d56Sopenharmony_ci """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) 16717db96d56Sopenharmony_ci 16727db96d56Sopenharmony_ci The year, month and day arguments are required. tzinfo may be None, or an 16737db96d56Sopenharmony_ci instance of a tzinfo subclass. The remaining arguments may be ints. 16747db96d56Sopenharmony_ci """ 16757db96d56Sopenharmony_ci __slots__ = date.__slots__ + time.__slots__ 16767db96d56Sopenharmony_ci 16777db96d56Sopenharmony_ci def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, 16787db96d56Sopenharmony_ci microsecond=0, tzinfo=None, *, fold=0): 16797db96d56Sopenharmony_ci if (isinstance(year, (bytes, str)) and len(year) == 10 and 16807db96d56Sopenharmony_ci 1 <= ord(year[2:3])&0x7F <= 12): 16817db96d56Sopenharmony_ci # Pickle support 16827db96d56Sopenharmony_ci if isinstance(year, str): 16837db96d56Sopenharmony_ci try: 16847db96d56Sopenharmony_ci year = bytes(year, 'latin1') 16857db96d56Sopenharmony_ci except UnicodeEncodeError: 16867db96d56Sopenharmony_ci # More informative error message. 16877db96d56Sopenharmony_ci raise ValueError( 16887db96d56Sopenharmony_ci "Failed to encode latin1 string when unpickling " 16897db96d56Sopenharmony_ci "a datetime object. " 16907db96d56Sopenharmony_ci "pickle.load(data, encoding='latin1') is assumed.") 16917db96d56Sopenharmony_ci self = object.__new__(cls) 16927db96d56Sopenharmony_ci self.__setstate(year, month) 16937db96d56Sopenharmony_ci self._hashcode = -1 16947db96d56Sopenharmony_ci return self 16957db96d56Sopenharmony_ci year, month, day = _check_date_fields(year, month, day) 16967db96d56Sopenharmony_ci hour, minute, second, microsecond, fold = _check_time_fields( 16977db96d56Sopenharmony_ci hour, minute, second, microsecond, fold) 16987db96d56Sopenharmony_ci _check_tzinfo_arg(tzinfo) 16997db96d56Sopenharmony_ci self = object.__new__(cls) 17007db96d56Sopenharmony_ci self._year = year 17017db96d56Sopenharmony_ci self._month = month 17027db96d56Sopenharmony_ci self._day = day 17037db96d56Sopenharmony_ci self._hour = hour 17047db96d56Sopenharmony_ci self._minute = minute 17057db96d56Sopenharmony_ci self._second = second 17067db96d56Sopenharmony_ci self._microsecond = microsecond 17077db96d56Sopenharmony_ci self._tzinfo = tzinfo 17087db96d56Sopenharmony_ci self._hashcode = -1 17097db96d56Sopenharmony_ci self._fold = fold 17107db96d56Sopenharmony_ci return self 17117db96d56Sopenharmony_ci 17127db96d56Sopenharmony_ci # Read-only field accessors 17137db96d56Sopenharmony_ci @property 17147db96d56Sopenharmony_ci def hour(self): 17157db96d56Sopenharmony_ci """hour (0-23)""" 17167db96d56Sopenharmony_ci return self._hour 17177db96d56Sopenharmony_ci 17187db96d56Sopenharmony_ci @property 17197db96d56Sopenharmony_ci def minute(self): 17207db96d56Sopenharmony_ci """minute (0-59)""" 17217db96d56Sopenharmony_ci return self._minute 17227db96d56Sopenharmony_ci 17237db96d56Sopenharmony_ci @property 17247db96d56Sopenharmony_ci def second(self): 17257db96d56Sopenharmony_ci """second (0-59)""" 17267db96d56Sopenharmony_ci return self._second 17277db96d56Sopenharmony_ci 17287db96d56Sopenharmony_ci @property 17297db96d56Sopenharmony_ci def microsecond(self): 17307db96d56Sopenharmony_ci """microsecond (0-999999)""" 17317db96d56Sopenharmony_ci return self._microsecond 17327db96d56Sopenharmony_ci 17337db96d56Sopenharmony_ci @property 17347db96d56Sopenharmony_ci def tzinfo(self): 17357db96d56Sopenharmony_ci """timezone info object""" 17367db96d56Sopenharmony_ci return self._tzinfo 17377db96d56Sopenharmony_ci 17387db96d56Sopenharmony_ci @property 17397db96d56Sopenharmony_ci def fold(self): 17407db96d56Sopenharmony_ci return self._fold 17417db96d56Sopenharmony_ci 17427db96d56Sopenharmony_ci @classmethod 17437db96d56Sopenharmony_ci def _fromtimestamp(cls, t, utc, tz): 17447db96d56Sopenharmony_ci """Construct a datetime from a POSIX timestamp (like time.time()). 17457db96d56Sopenharmony_ci 17467db96d56Sopenharmony_ci A timezone info object may be passed in as well. 17477db96d56Sopenharmony_ci """ 17487db96d56Sopenharmony_ci frac, t = _math.modf(t) 17497db96d56Sopenharmony_ci us = round(frac * 1e6) 17507db96d56Sopenharmony_ci if us >= 1000000: 17517db96d56Sopenharmony_ci t += 1 17527db96d56Sopenharmony_ci us -= 1000000 17537db96d56Sopenharmony_ci elif us < 0: 17547db96d56Sopenharmony_ci t -= 1 17557db96d56Sopenharmony_ci us += 1000000 17567db96d56Sopenharmony_ci 17577db96d56Sopenharmony_ci converter = _time.gmtime if utc else _time.localtime 17587db96d56Sopenharmony_ci y, m, d, hh, mm, ss, weekday, jday, dst = converter(t) 17597db96d56Sopenharmony_ci ss = min(ss, 59) # clamp out leap seconds if the platform has them 17607db96d56Sopenharmony_ci result = cls(y, m, d, hh, mm, ss, us, tz) 17617db96d56Sopenharmony_ci if tz is None and not utc: 17627db96d56Sopenharmony_ci # As of version 2015f max fold in IANA database is 17637db96d56Sopenharmony_ci # 23 hours at 1969-09-30 13:00:00 in Kwajalein. 17647db96d56Sopenharmony_ci # Let's probe 24 hours in the past to detect a transition: 17657db96d56Sopenharmony_ci max_fold_seconds = 24 * 3600 17667db96d56Sopenharmony_ci 17677db96d56Sopenharmony_ci # On Windows localtime_s throws an OSError for negative values, 17687db96d56Sopenharmony_ci # thus we can't perform fold detection for values of time less 17697db96d56Sopenharmony_ci # than the max time fold. See comments in _datetimemodule's 17707db96d56Sopenharmony_ci # version of this method for more details. 17717db96d56Sopenharmony_ci if t < max_fold_seconds and sys.platform.startswith("win"): 17727db96d56Sopenharmony_ci return result 17737db96d56Sopenharmony_ci 17747db96d56Sopenharmony_ci y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6] 17757db96d56Sopenharmony_ci probe1 = cls(y, m, d, hh, mm, ss, us, tz) 17767db96d56Sopenharmony_ci trans = result - probe1 - timedelta(0, max_fold_seconds) 17777db96d56Sopenharmony_ci if trans.days < 0: 17787db96d56Sopenharmony_ci y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6] 17797db96d56Sopenharmony_ci probe2 = cls(y, m, d, hh, mm, ss, us, tz) 17807db96d56Sopenharmony_ci if probe2 == result: 17817db96d56Sopenharmony_ci result._fold = 1 17827db96d56Sopenharmony_ci elif tz is not None: 17837db96d56Sopenharmony_ci result = tz.fromutc(result) 17847db96d56Sopenharmony_ci return result 17857db96d56Sopenharmony_ci 17867db96d56Sopenharmony_ci @classmethod 17877db96d56Sopenharmony_ci def fromtimestamp(cls, t, tz=None): 17887db96d56Sopenharmony_ci """Construct a datetime from a POSIX timestamp (like time.time()). 17897db96d56Sopenharmony_ci 17907db96d56Sopenharmony_ci A timezone info object may be passed in as well. 17917db96d56Sopenharmony_ci """ 17927db96d56Sopenharmony_ci _check_tzinfo_arg(tz) 17937db96d56Sopenharmony_ci 17947db96d56Sopenharmony_ci return cls._fromtimestamp(t, tz is not None, tz) 17957db96d56Sopenharmony_ci 17967db96d56Sopenharmony_ci @classmethod 17977db96d56Sopenharmony_ci def utcfromtimestamp(cls, t): 17987db96d56Sopenharmony_ci """Construct a naive UTC datetime from a POSIX timestamp.""" 17997db96d56Sopenharmony_ci return cls._fromtimestamp(t, True, None) 18007db96d56Sopenharmony_ci 18017db96d56Sopenharmony_ci @classmethod 18027db96d56Sopenharmony_ci def now(cls, tz=None): 18037db96d56Sopenharmony_ci "Construct a datetime from time.time() and optional time zone info." 18047db96d56Sopenharmony_ci t = _time.time() 18057db96d56Sopenharmony_ci return cls.fromtimestamp(t, tz) 18067db96d56Sopenharmony_ci 18077db96d56Sopenharmony_ci @classmethod 18087db96d56Sopenharmony_ci def utcnow(cls): 18097db96d56Sopenharmony_ci "Construct a UTC datetime from time.time()." 18107db96d56Sopenharmony_ci t = _time.time() 18117db96d56Sopenharmony_ci return cls.utcfromtimestamp(t) 18127db96d56Sopenharmony_ci 18137db96d56Sopenharmony_ci @classmethod 18147db96d56Sopenharmony_ci def combine(cls, date, time, tzinfo=True): 18157db96d56Sopenharmony_ci "Construct a datetime from a given date and a given time." 18167db96d56Sopenharmony_ci if not isinstance(date, _date_class): 18177db96d56Sopenharmony_ci raise TypeError("date argument must be a date instance") 18187db96d56Sopenharmony_ci if not isinstance(time, _time_class): 18197db96d56Sopenharmony_ci raise TypeError("time argument must be a time instance") 18207db96d56Sopenharmony_ci if tzinfo is True: 18217db96d56Sopenharmony_ci tzinfo = time.tzinfo 18227db96d56Sopenharmony_ci return cls(date.year, date.month, date.day, 18237db96d56Sopenharmony_ci time.hour, time.minute, time.second, time.microsecond, 18247db96d56Sopenharmony_ci tzinfo, fold=time.fold) 18257db96d56Sopenharmony_ci 18267db96d56Sopenharmony_ci @classmethod 18277db96d56Sopenharmony_ci def fromisoformat(cls, date_string): 18287db96d56Sopenharmony_ci """Construct a datetime from a string in one of the ISO 8601 formats.""" 18297db96d56Sopenharmony_ci if not isinstance(date_string, str): 18307db96d56Sopenharmony_ci raise TypeError('fromisoformat: argument must be str') 18317db96d56Sopenharmony_ci 18327db96d56Sopenharmony_ci if len(date_string) < 7: 18337db96d56Sopenharmony_ci raise ValueError(f'Invalid isoformat string: {date_string!r}') 18347db96d56Sopenharmony_ci 18357db96d56Sopenharmony_ci # Split this at the separator 18367db96d56Sopenharmony_ci try: 18377db96d56Sopenharmony_ci separator_location = _find_isoformat_datetime_separator(date_string) 18387db96d56Sopenharmony_ci dstr = date_string[0:separator_location] 18397db96d56Sopenharmony_ci tstr = date_string[(separator_location+1):] 18407db96d56Sopenharmony_ci 18417db96d56Sopenharmony_ci date_components = _parse_isoformat_date(dstr) 18427db96d56Sopenharmony_ci except ValueError: 18437db96d56Sopenharmony_ci raise ValueError( 18447db96d56Sopenharmony_ci f'Invalid isoformat string: {date_string!r}') from None 18457db96d56Sopenharmony_ci 18467db96d56Sopenharmony_ci if tstr: 18477db96d56Sopenharmony_ci try: 18487db96d56Sopenharmony_ci time_components = _parse_isoformat_time(tstr) 18497db96d56Sopenharmony_ci except ValueError: 18507db96d56Sopenharmony_ci raise ValueError( 18517db96d56Sopenharmony_ci f'Invalid isoformat string: {date_string!r}') from None 18527db96d56Sopenharmony_ci else: 18537db96d56Sopenharmony_ci time_components = [0, 0, 0, 0, None] 18547db96d56Sopenharmony_ci 18557db96d56Sopenharmony_ci return cls(*(date_components + time_components)) 18567db96d56Sopenharmony_ci 18577db96d56Sopenharmony_ci def timetuple(self): 18587db96d56Sopenharmony_ci "Return local time tuple compatible with time.localtime()." 18597db96d56Sopenharmony_ci dst = self.dst() 18607db96d56Sopenharmony_ci if dst is None: 18617db96d56Sopenharmony_ci dst = -1 18627db96d56Sopenharmony_ci elif dst: 18637db96d56Sopenharmony_ci dst = 1 18647db96d56Sopenharmony_ci else: 18657db96d56Sopenharmony_ci dst = 0 18667db96d56Sopenharmony_ci return _build_struct_time(self.year, self.month, self.day, 18677db96d56Sopenharmony_ci self.hour, self.minute, self.second, 18687db96d56Sopenharmony_ci dst) 18697db96d56Sopenharmony_ci 18707db96d56Sopenharmony_ci def _mktime(self): 18717db96d56Sopenharmony_ci """Return integer POSIX timestamp.""" 18727db96d56Sopenharmony_ci epoch = datetime(1970, 1, 1) 18737db96d56Sopenharmony_ci max_fold_seconds = 24 * 3600 18747db96d56Sopenharmony_ci t = (self - epoch) // timedelta(0, 1) 18757db96d56Sopenharmony_ci def local(u): 18767db96d56Sopenharmony_ci y, m, d, hh, mm, ss = _time.localtime(u)[:6] 18777db96d56Sopenharmony_ci return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1) 18787db96d56Sopenharmony_ci 18797db96d56Sopenharmony_ci # Our goal is to solve t = local(u) for u. 18807db96d56Sopenharmony_ci a = local(t) - t 18817db96d56Sopenharmony_ci u1 = t - a 18827db96d56Sopenharmony_ci t1 = local(u1) 18837db96d56Sopenharmony_ci if t1 == t: 18847db96d56Sopenharmony_ci # We found one solution, but it may not be the one we need. 18857db96d56Sopenharmony_ci # Look for an earlier solution (if `fold` is 0), or a 18867db96d56Sopenharmony_ci # later one (if `fold` is 1). 18877db96d56Sopenharmony_ci u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold] 18887db96d56Sopenharmony_ci b = local(u2) - u2 18897db96d56Sopenharmony_ci if a == b: 18907db96d56Sopenharmony_ci return u1 18917db96d56Sopenharmony_ci else: 18927db96d56Sopenharmony_ci b = t1 - u1 18937db96d56Sopenharmony_ci assert a != b 18947db96d56Sopenharmony_ci u2 = t - b 18957db96d56Sopenharmony_ci t2 = local(u2) 18967db96d56Sopenharmony_ci if t2 == t: 18977db96d56Sopenharmony_ci return u2 18987db96d56Sopenharmony_ci if t1 == t: 18997db96d56Sopenharmony_ci return u1 19007db96d56Sopenharmony_ci # We have found both offsets a and b, but neither t - a nor t - b is 19017db96d56Sopenharmony_ci # a solution. This means t is in the gap. 19027db96d56Sopenharmony_ci return (max, min)[self.fold](u1, u2) 19037db96d56Sopenharmony_ci 19047db96d56Sopenharmony_ci 19057db96d56Sopenharmony_ci def timestamp(self): 19067db96d56Sopenharmony_ci "Return POSIX timestamp as float" 19077db96d56Sopenharmony_ci if self._tzinfo is None: 19087db96d56Sopenharmony_ci s = self._mktime() 19097db96d56Sopenharmony_ci return s + self.microsecond / 1e6 19107db96d56Sopenharmony_ci else: 19117db96d56Sopenharmony_ci return (self - _EPOCH).total_seconds() 19127db96d56Sopenharmony_ci 19137db96d56Sopenharmony_ci def utctimetuple(self): 19147db96d56Sopenharmony_ci "Return UTC time tuple compatible with time.gmtime()." 19157db96d56Sopenharmony_ci offset = self.utcoffset() 19167db96d56Sopenharmony_ci if offset: 19177db96d56Sopenharmony_ci self -= offset 19187db96d56Sopenharmony_ci y, m, d = self.year, self.month, self.day 19197db96d56Sopenharmony_ci hh, mm, ss = self.hour, self.minute, self.second 19207db96d56Sopenharmony_ci return _build_struct_time(y, m, d, hh, mm, ss, 0) 19217db96d56Sopenharmony_ci 19227db96d56Sopenharmony_ci def date(self): 19237db96d56Sopenharmony_ci "Return the date part." 19247db96d56Sopenharmony_ci return date(self._year, self._month, self._day) 19257db96d56Sopenharmony_ci 19267db96d56Sopenharmony_ci def time(self): 19277db96d56Sopenharmony_ci "Return the time part, with tzinfo None." 19287db96d56Sopenharmony_ci return time(self.hour, self.minute, self.second, self.microsecond, fold=self.fold) 19297db96d56Sopenharmony_ci 19307db96d56Sopenharmony_ci def timetz(self): 19317db96d56Sopenharmony_ci "Return the time part, with same tzinfo." 19327db96d56Sopenharmony_ci return time(self.hour, self.minute, self.second, self.microsecond, 19337db96d56Sopenharmony_ci self._tzinfo, fold=self.fold) 19347db96d56Sopenharmony_ci 19357db96d56Sopenharmony_ci def replace(self, year=None, month=None, day=None, hour=None, 19367db96d56Sopenharmony_ci minute=None, second=None, microsecond=None, tzinfo=True, 19377db96d56Sopenharmony_ci *, fold=None): 19387db96d56Sopenharmony_ci """Return a new datetime with new values for the specified fields.""" 19397db96d56Sopenharmony_ci if year is None: 19407db96d56Sopenharmony_ci year = self.year 19417db96d56Sopenharmony_ci if month is None: 19427db96d56Sopenharmony_ci month = self.month 19437db96d56Sopenharmony_ci if day is None: 19447db96d56Sopenharmony_ci day = self.day 19457db96d56Sopenharmony_ci if hour is None: 19467db96d56Sopenharmony_ci hour = self.hour 19477db96d56Sopenharmony_ci if minute is None: 19487db96d56Sopenharmony_ci minute = self.minute 19497db96d56Sopenharmony_ci if second is None: 19507db96d56Sopenharmony_ci second = self.second 19517db96d56Sopenharmony_ci if microsecond is None: 19527db96d56Sopenharmony_ci microsecond = self.microsecond 19537db96d56Sopenharmony_ci if tzinfo is True: 19547db96d56Sopenharmony_ci tzinfo = self.tzinfo 19557db96d56Sopenharmony_ci if fold is None: 19567db96d56Sopenharmony_ci fold = self.fold 19577db96d56Sopenharmony_ci return type(self)(year, month, day, hour, minute, second, 19587db96d56Sopenharmony_ci microsecond, tzinfo, fold=fold) 19597db96d56Sopenharmony_ci 19607db96d56Sopenharmony_ci def _local_timezone(self): 19617db96d56Sopenharmony_ci if self.tzinfo is None: 19627db96d56Sopenharmony_ci ts = self._mktime() 19637db96d56Sopenharmony_ci else: 19647db96d56Sopenharmony_ci ts = (self - _EPOCH) // timedelta(seconds=1) 19657db96d56Sopenharmony_ci localtm = _time.localtime(ts) 19667db96d56Sopenharmony_ci local = datetime(*localtm[:6]) 19677db96d56Sopenharmony_ci # Extract TZ data 19687db96d56Sopenharmony_ci gmtoff = localtm.tm_gmtoff 19697db96d56Sopenharmony_ci zone = localtm.tm_zone 19707db96d56Sopenharmony_ci return timezone(timedelta(seconds=gmtoff), zone) 19717db96d56Sopenharmony_ci 19727db96d56Sopenharmony_ci def astimezone(self, tz=None): 19737db96d56Sopenharmony_ci if tz is None: 19747db96d56Sopenharmony_ci tz = self._local_timezone() 19757db96d56Sopenharmony_ci elif not isinstance(tz, tzinfo): 19767db96d56Sopenharmony_ci raise TypeError("tz argument must be an instance of tzinfo") 19777db96d56Sopenharmony_ci 19787db96d56Sopenharmony_ci mytz = self.tzinfo 19797db96d56Sopenharmony_ci if mytz is None: 19807db96d56Sopenharmony_ci mytz = self._local_timezone() 19817db96d56Sopenharmony_ci myoffset = mytz.utcoffset(self) 19827db96d56Sopenharmony_ci else: 19837db96d56Sopenharmony_ci myoffset = mytz.utcoffset(self) 19847db96d56Sopenharmony_ci if myoffset is None: 19857db96d56Sopenharmony_ci mytz = self.replace(tzinfo=None)._local_timezone() 19867db96d56Sopenharmony_ci myoffset = mytz.utcoffset(self) 19877db96d56Sopenharmony_ci 19887db96d56Sopenharmony_ci if tz is mytz: 19897db96d56Sopenharmony_ci return self 19907db96d56Sopenharmony_ci 19917db96d56Sopenharmony_ci # Convert self to UTC, and attach the new time zone object. 19927db96d56Sopenharmony_ci utc = (self - myoffset).replace(tzinfo=tz) 19937db96d56Sopenharmony_ci 19947db96d56Sopenharmony_ci # Convert from UTC to tz's local time. 19957db96d56Sopenharmony_ci return tz.fromutc(utc) 19967db96d56Sopenharmony_ci 19977db96d56Sopenharmony_ci # Ways to produce a string. 19987db96d56Sopenharmony_ci 19997db96d56Sopenharmony_ci def ctime(self): 20007db96d56Sopenharmony_ci "Return ctime() style string." 20017db96d56Sopenharmony_ci weekday = self.toordinal() % 7 or 7 20027db96d56Sopenharmony_ci return "%s %s %2d %02d:%02d:%02d %04d" % ( 20037db96d56Sopenharmony_ci _DAYNAMES[weekday], 20047db96d56Sopenharmony_ci _MONTHNAMES[self._month], 20057db96d56Sopenharmony_ci self._day, 20067db96d56Sopenharmony_ci self._hour, self._minute, self._second, 20077db96d56Sopenharmony_ci self._year) 20087db96d56Sopenharmony_ci 20097db96d56Sopenharmony_ci def isoformat(self, sep='T', timespec='auto'): 20107db96d56Sopenharmony_ci """Return the time formatted according to ISO. 20117db96d56Sopenharmony_ci 20127db96d56Sopenharmony_ci The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'. 20137db96d56Sopenharmony_ci By default, the fractional part is omitted if self.microsecond == 0. 20147db96d56Sopenharmony_ci 20157db96d56Sopenharmony_ci If self.tzinfo is not None, the UTC offset is also attached, giving 20167db96d56Sopenharmony_ci giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'. 20177db96d56Sopenharmony_ci 20187db96d56Sopenharmony_ci Optional argument sep specifies the separator between date and 20197db96d56Sopenharmony_ci time, default 'T'. 20207db96d56Sopenharmony_ci 20217db96d56Sopenharmony_ci The optional argument timespec specifies the number of additional 20227db96d56Sopenharmony_ci terms of the time to include. Valid options are 'auto', 'hours', 20237db96d56Sopenharmony_ci 'minutes', 'seconds', 'milliseconds' and 'microseconds'. 20247db96d56Sopenharmony_ci """ 20257db96d56Sopenharmony_ci s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, sep) + 20267db96d56Sopenharmony_ci _format_time(self._hour, self._minute, self._second, 20277db96d56Sopenharmony_ci self._microsecond, timespec)) 20287db96d56Sopenharmony_ci 20297db96d56Sopenharmony_ci off = self.utcoffset() 20307db96d56Sopenharmony_ci tz = _format_offset(off) 20317db96d56Sopenharmony_ci if tz: 20327db96d56Sopenharmony_ci s += tz 20337db96d56Sopenharmony_ci 20347db96d56Sopenharmony_ci return s 20357db96d56Sopenharmony_ci 20367db96d56Sopenharmony_ci def __repr__(self): 20377db96d56Sopenharmony_ci """Convert to formal string, for repr().""" 20387db96d56Sopenharmony_ci L = [self._year, self._month, self._day, # These are never zero 20397db96d56Sopenharmony_ci self._hour, self._minute, self._second, self._microsecond] 20407db96d56Sopenharmony_ci if L[-1] == 0: 20417db96d56Sopenharmony_ci del L[-1] 20427db96d56Sopenharmony_ci if L[-1] == 0: 20437db96d56Sopenharmony_ci del L[-1] 20447db96d56Sopenharmony_ci s = "%s.%s(%s)" % (self.__class__.__module__, 20457db96d56Sopenharmony_ci self.__class__.__qualname__, 20467db96d56Sopenharmony_ci ", ".join(map(str, L))) 20477db96d56Sopenharmony_ci if self._tzinfo is not None: 20487db96d56Sopenharmony_ci assert s[-1:] == ")" 20497db96d56Sopenharmony_ci s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" 20507db96d56Sopenharmony_ci if self._fold: 20517db96d56Sopenharmony_ci assert s[-1:] == ")" 20527db96d56Sopenharmony_ci s = s[:-1] + ", fold=1)" 20537db96d56Sopenharmony_ci return s 20547db96d56Sopenharmony_ci 20557db96d56Sopenharmony_ci def __str__(self): 20567db96d56Sopenharmony_ci "Convert to string, for str()." 20577db96d56Sopenharmony_ci return self.isoformat(sep=' ') 20587db96d56Sopenharmony_ci 20597db96d56Sopenharmony_ci @classmethod 20607db96d56Sopenharmony_ci def strptime(cls, date_string, format): 20617db96d56Sopenharmony_ci 'string, format -> new datetime parsed from a string (like time.strptime()).' 20627db96d56Sopenharmony_ci import _strptime 20637db96d56Sopenharmony_ci return _strptime._strptime_datetime(cls, date_string, format) 20647db96d56Sopenharmony_ci 20657db96d56Sopenharmony_ci def utcoffset(self): 20667db96d56Sopenharmony_ci """Return the timezone offset as timedelta positive east of UTC (negative west of 20677db96d56Sopenharmony_ci UTC).""" 20687db96d56Sopenharmony_ci if self._tzinfo is None: 20697db96d56Sopenharmony_ci return None 20707db96d56Sopenharmony_ci offset = self._tzinfo.utcoffset(self) 20717db96d56Sopenharmony_ci _check_utc_offset("utcoffset", offset) 20727db96d56Sopenharmony_ci return offset 20737db96d56Sopenharmony_ci 20747db96d56Sopenharmony_ci def tzname(self): 20757db96d56Sopenharmony_ci """Return the timezone name. 20767db96d56Sopenharmony_ci 20777db96d56Sopenharmony_ci Note that the name is 100% informational -- there's no requirement that 20787db96d56Sopenharmony_ci it mean anything in particular. For example, "GMT", "UTC", "-500", 20797db96d56Sopenharmony_ci "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. 20807db96d56Sopenharmony_ci """ 20817db96d56Sopenharmony_ci if self._tzinfo is None: 20827db96d56Sopenharmony_ci return None 20837db96d56Sopenharmony_ci name = self._tzinfo.tzname(self) 20847db96d56Sopenharmony_ci _check_tzname(name) 20857db96d56Sopenharmony_ci return name 20867db96d56Sopenharmony_ci 20877db96d56Sopenharmony_ci def dst(self): 20887db96d56Sopenharmony_ci """Return 0 if DST is not in effect, or the DST offset (as timedelta 20897db96d56Sopenharmony_ci positive eastward) if DST is in effect. 20907db96d56Sopenharmony_ci 20917db96d56Sopenharmony_ci This is purely informational; the DST offset has already been added to 20927db96d56Sopenharmony_ci the UTC offset returned by utcoffset() if applicable, so there's no 20937db96d56Sopenharmony_ci need to consult dst() unless you're interested in displaying the DST 20947db96d56Sopenharmony_ci info. 20957db96d56Sopenharmony_ci """ 20967db96d56Sopenharmony_ci if self._tzinfo is None: 20977db96d56Sopenharmony_ci return None 20987db96d56Sopenharmony_ci offset = self._tzinfo.dst(self) 20997db96d56Sopenharmony_ci _check_utc_offset("dst", offset) 21007db96d56Sopenharmony_ci return offset 21017db96d56Sopenharmony_ci 21027db96d56Sopenharmony_ci # Comparisons of datetime objects with other. 21037db96d56Sopenharmony_ci 21047db96d56Sopenharmony_ci def __eq__(self, other): 21057db96d56Sopenharmony_ci if isinstance(other, datetime): 21067db96d56Sopenharmony_ci return self._cmp(other, allow_mixed=True) == 0 21077db96d56Sopenharmony_ci elif not isinstance(other, date): 21087db96d56Sopenharmony_ci return NotImplemented 21097db96d56Sopenharmony_ci else: 21107db96d56Sopenharmony_ci return False 21117db96d56Sopenharmony_ci 21127db96d56Sopenharmony_ci def __le__(self, other): 21137db96d56Sopenharmony_ci if isinstance(other, datetime): 21147db96d56Sopenharmony_ci return self._cmp(other) <= 0 21157db96d56Sopenharmony_ci elif not isinstance(other, date): 21167db96d56Sopenharmony_ci return NotImplemented 21177db96d56Sopenharmony_ci else: 21187db96d56Sopenharmony_ci _cmperror(self, other) 21197db96d56Sopenharmony_ci 21207db96d56Sopenharmony_ci def __lt__(self, other): 21217db96d56Sopenharmony_ci if isinstance(other, datetime): 21227db96d56Sopenharmony_ci return self._cmp(other) < 0 21237db96d56Sopenharmony_ci elif not isinstance(other, date): 21247db96d56Sopenharmony_ci return NotImplemented 21257db96d56Sopenharmony_ci else: 21267db96d56Sopenharmony_ci _cmperror(self, other) 21277db96d56Sopenharmony_ci 21287db96d56Sopenharmony_ci def __ge__(self, other): 21297db96d56Sopenharmony_ci if isinstance(other, datetime): 21307db96d56Sopenharmony_ci return self._cmp(other) >= 0 21317db96d56Sopenharmony_ci elif not isinstance(other, date): 21327db96d56Sopenharmony_ci return NotImplemented 21337db96d56Sopenharmony_ci else: 21347db96d56Sopenharmony_ci _cmperror(self, other) 21357db96d56Sopenharmony_ci 21367db96d56Sopenharmony_ci def __gt__(self, other): 21377db96d56Sopenharmony_ci if isinstance(other, datetime): 21387db96d56Sopenharmony_ci return self._cmp(other) > 0 21397db96d56Sopenharmony_ci elif not isinstance(other, date): 21407db96d56Sopenharmony_ci return NotImplemented 21417db96d56Sopenharmony_ci else: 21427db96d56Sopenharmony_ci _cmperror(self, other) 21437db96d56Sopenharmony_ci 21447db96d56Sopenharmony_ci def _cmp(self, other, allow_mixed=False): 21457db96d56Sopenharmony_ci assert isinstance(other, datetime) 21467db96d56Sopenharmony_ci mytz = self._tzinfo 21477db96d56Sopenharmony_ci ottz = other._tzinfo 21487db96d56Sopenharmony_ci myoff = otoff = None 21497db96d56Sopenharmony_ci 21507db96d56Sopenharmony_ci if mytz is ottz: 21517db96d56Sopenharmony_ci base_compare = True 21527db96d56Sopenharmony_ci else: 21537db96d56Sopenharmony_ci myoff = self.utcoffset() 21547db96d56Sopenharmony_ci otoff = other.utcoffset() 21557db96d56Sopenharmony_ci # Assume that allow_mixed means that we are called from __eq__ 21567db96d56Sopenharmony_ci if allow_mixed: 21577db96d56Sopenharmony_ci if myoff != self.replace(fold=not self.fold).utcoffset(): 21587db96d56Sopenharmony_ci return 2 21597db96d56Sopenharmony_ci if otoff != other.replace(fold=not other.fold).utcoffset(): 21607db96d56Sopenharmony_ci return 2 21617db96d56Sopenharmony_ci base_compare = myoff == otoff 21627db96d56Sopenharmony_ci 21637db96d56Sopenharmony_ci if base_compare: 21647db96d56Sopenharmony_ci return _cmp((self._year, self._month, self._day, 21657db96d56Sopenharmony_ci self._hour, self._minute, self._second, 21667db96d56Sopenharmony_ci self._microsecond), 21677db96d56Sopenharmony_ci (other._year, other._month, other._day, 21687db96d56Sopenharmony_ci other._hour, other._minute, other._second, 21697db96d56Sopenharmony_ci other._microsecond)) 21707db96d56Sopenharmony_ci if myoff is None or otoff is None: 21717db96d56Sopenharmony_ci if allow_mixed: 21727db96d56Sopenharmony_ci return 2 # arbitrary non-zero value 21737db96d56Sopenharmony_ci else: 21747db96d56Sopenharmony_ci raise TypeError("cannot compare naive and aware datetimes") 21757db96d56Sopenharmony_ci # XXX What follows could be done more efficiently... 21767db96d56Sopenharmony_ci diff = self - other # this will take offsets into account 21777db96d56Sopenharmony_ci if diff.days < 0: 21787db96d56Sopenharmony_ci return -1 21797db96d56Sopenharmony_ci return diff and 1 or 0 21807db96d56Sopenharmony_ci 21817db96d56Sopenharmony_ci def __add__(self, other): 21827db96d56Sopenharmony_ci "Add a datetime and a timedelta." 21837db96d56Sopenharmony_ci if not isinstance(other, timedelta): 21847db96d56Sopenharmony_ci return NotImplemented 21857db96d56Sopenharmony_ci delta = timedelta(self.toordinal(), 21867db96d56Sopenharmony_ci hours=self._hour, 21877db96d56Sopenharmony_ci minutes=self._minute, 21887db96d56Sopenharmony_ci seconds=self._second, 21897db96d56Sopenharmony_ci microseconds=self._microsecond) 21907db96d56Sopenharmony_ci delta += other 21917db96d56Sopenharmony_ci hour, rem = divmod(delta.seconds, 3600) 21927db96d56Sopenharmony_ci minute, second = divmod(rem, 60) 21937db96d56Sopenharmony_ci if 0 < delta.days <= _MAXORDINAL: 21947db96d56Sopenharmony_ci return type(self).combine(date.fromordinal(delta.days), 21957db96d56Sopenharmony_ci time(hour, minute, second, 21967db96d56Sopenharmony_ci delta.microseconds, 21977db96d56Sopenharmony_ci tzinfo=self._tzinfo)) 21987db96d56Sopenharmony_ci raise OverflowError("result out of range") 21997db96d56Sopenharmony_ci 22007db96d56Sopenharmony_ci __radd__ = __add__ 22017db96d56Sopenharmony_ci 22027db96d56Sopenharmony_ci def __sub__(self, other): 22037db96d56Sopenharmony_ci "Subtract two datetimes, or a datetime and a timedelta." 22047db96d56Sopenharmony_ci if not isinstance(other, datetime): 22057db96d56Sopenharmony_ci if isinstance(other, timedelta): 22067db96d56Sopenharmony_ci return self + -other 22077db96d56Sopenharmony_ci return NotImplemented 22087db96d56Sopenharmony_ci 22097db96d56Sopenharmony_ci days1 = self.toordinal() 22107db96d56Sopenharmony_ci days2 = other.toordinal() 22117db96d56Sopenharmony_ci secs1 = self._second + self._minute * 60 + self._hour * 3600 22127db96d56Sopenharmony_ci secs2 = other._second + other._minute * 60 + other._hour * 3600 22137db96d56Sopenharmony_ci base = timedelta(days1 - days2, 22147db96d56Sopenharmony_ci secs1 - secs2, 22157db96d56Sopenharmony_ci self._microsecond - other._microsecond) 22167db96d56Sopenharmony_ci if self._tzinfo is other._tzinfo: 22177db96d56Sopenharmony_ci return base 22187db96d56Sopenharmony_ci myoff = self.utcoffset() 22197db96d56Sopenharmony_ci otoff = other.utcoffset() 22207db96d56Sopenharmony_ci if myoff == otoff: 22217db96d56Sopenharmony_ci return base 22227db96d56Sopenharmony_ci if myoff is None or otoff is None: 22237db96d56Sopenharmony_ci raise TypeError("cannot mix naive and timezone-aware time") 22247db96d56Sopenharmony_ci return base + otoff - myoff 22257db96d56Sopenharmony_ci 22267db96d56Sopenharmony_ci def __hash__(self): 22277db96d56Sopenharmony_ci if self._hashcode == -1: 22287db96d56Sopenharmony_ci if self.fold: 22297db96d56Sopenharmony_ci t = self.replace(fold=0) 22307db96d56Sopenharmony_ci else: 22317db96d56Sopenharmony_ci t = self 22327db96d56Sopenharmony_ci tzoff = t.utcoffset() 22337db96d56Sopenharmony_ci if tzoff is None: 22347db96d56Sopenharmony_ci self._hashcode = hash(t._getstate()[0]) 22357db96d56Sopenharmony_ci else: 22367db96d56Sopenharmony_ci days = _ymd2ord(self.year, self.month, self.day) 22377db96d56Sopenharmony_ci seconds = self.hour * 3600 + self.minute * 60 + self.second 22387db96d56Sopenharmony_ci self._hashcode = hash(timedelta(days, seconds, self.microsecond) - tzoff) 22397db96d56Sopenharmony_ci return self._hashcode 22407db96d56Sopenharmony_ci 22417db96d56Sopenharmony_ci # Pickle support. 22427db96d56Sopenharmony_ci 22437db96d56Sopenharmony_ci def _getstate(self, protocol=3): 22447db96d56Sopenharmony_ci yhi, ylo = divmod(self._year, 256) 22457db96d56Sopenharmony_ci us2, us3 = divmod(self._microsecond, 256) 22467db96d56Sopenharmony_ci us1, us2 = divmod(us2, 256) 22477db96d56Sopenharmony_ci m = self._month 22487db96d56Sopenharmony_ci if self._fold and protocol > 3: 22497db96d56Sopenharmony_ci m += 128 22507db96d56Sopenharmony_ci basestate = bytes([yhi, ylo, m, self._day, 22517db96d56Sopenharmony_ci self._hour, self._minute, self._second, 22527db96d56Sopenharmony_ci us1, us2, us3]) 22537db96d56Sopenharmony_ci if self._tzinfo is None: 22547db96d56Sopenharmony_ci return (basestate,) 22557db96d56Sopenharmony_ci else: 22567db96d56Sopenharmony_ci return (basestate, self._tzinfo) 22577db96d56Sopenharmony_ci 22587db96d56Sopenharmony_ci def __setstate(self, string, tzinfo): 22597db96d56Sopenharmony_ci if tzinfo is not None and not isinstance(tzinfo, _tzinfo_class): 22607db96d56Sopenharmony_ci raise TypeError("bad tzinfo state arg") 22617db96d56Sopenharmony_ci (yhi, ylo, m, self._day, self._hour, 22627db96d56Sopenharmony_ci self._minute, self._second, us1, us2, us3) = string 22637db96d56Sopenharmony_ci if m > 127: 22647db96d56Sopenharmony_ci self._fold = 1 22657db96d56Sopenharmony_ci self._month = m - 128 22667db96d56Sopenharmony_ci else: 22677db96d56Sopenharmony_ci self._fold = 0 22687db96d56Sopenharmony_ci self._month = m 22697db96d56Sopenharmony_ci self._year = yhi * 256 + ylo 22707db96d56Sopenharmony_ci self._microsecond = (((us1 << 8) | us2) << 8) | us3 22717db96d56Sopenharmony_ci self._tzinfo = tzinfo 22727db96d56Sopenharmony_ci 22737db96d56Sopenharmony_ci def __reduce_ex__(self, protocol): 22747db96d56Sopenharmony_ci return (self.__class__, self._getstate(protocol)) 22757db96d56Sopenharmony_ci 22767db96d56Sopenharmony_ci def __reduce__(self): 22777db96d56Sopenharmony_ci return self.__reduce_ex__(2) 22787db96d56Sopenharmony_ci 22797db96d56Sopenharmony_ci 22807db96d56Sopenharmony_cidatetime.min = datetime(1, 1, 1) 22817db96d56Sopenharmony_cidatetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) 22827db96d56Sopenharmony_cidatetime.resolution = timedelta(microseconds=1) 22837db96d56Sopenharmony_ci 22847db96d56Sopenharmony_ci 22857db96d56Sopenharmony_cidef _isoweek1monday(year): 22867db96d56Sopenharmony_ci # Helper to calculate the day number of the Monday starting week 1 22877db96d56Sopenharmony_ci # XXX This could be done more efficiently 22887db96d56Sopenharmony_ci THURSDAY = 3 22897db96d56Sopenharmony_ci firstday = _ymd2ord(year, 1, 1) 22907db96d56Sopenharmony_ci firstweekday = (firstday + 6) % 7 # See weekday() above 22917db96d56Sopenharmony_ci week1monday = firstday - firstweekday 22927db96d56Sopenharmony_ci if firstweekday > THURSDAY: 22937db96d56Sopenharmony_ci week1monday += 7 22947db96d56Sopenharmony_ci return week1monday 22957db96d56Sopenharmony_ci 22967db96d56Sopenharmony_ci 22977db96d56Sopenharmony_ciclass timezone(tzinfo): 22987db96d56Sopenharmony_ci __slots__ = '_offset', '_name' 22997db96d56Sopenharmony_ci 23007db96d56Sopenharmony_ci # Sentinel value to disallow None 23017db96d56Sopenharmony_ci _Omitted = object() 23027db96d56Sopenharmony_ci def __new__(cls, offset, name=_Omitted): 23037db96d56Sopenharmony_ci if not isinstance(offset, timedelta): 23047db96d56Sopenharmony_ci raise TypeError("offset must be a timedelta") 23057db96d56Sopenharmony_ci if name is cls._Omitted: 23067db96d56Sopenharmony_ci if not offset: 23077db96d56Sopenharmony_ci return cls.utc 23087db96d56Sopenharmony_ci name = None 23097db96d56Sopenharmony_ci elif not isinstance(name, str): 23107db96d56Sopenharmony_ci raise TypeError("name must be a string") 23117db96d56Sopenharmony_ci if not cls._minoffset <= offset <= cls._maxoffset: 23127db96d56Sopenharmony_ci raise ValueError("offset must be a timedelta " 23137db96d56Sopenharmony_ci "strictly between -timedelta(hours=24) and " 23147db96d56Sopenharmony_ci "timedelta(hours=24).") 23157db96d56Sopenharmony_ci return cls._create(offset, name) 23167db96d56Sopenharmony_ci 23177db96d56Sopenharmony_ci @classmethod 23187db96d56Sopenharmony_ci def _create(cls, offset, name=None): 23197db96d56Sopenharmony_ci self = tzinfo.__new__(cls) 23207db96d56Sopenharmony_ci self._offset = offset 23217db96d56Sopenharmony_ci self._name = name 23227db96d56Sopenharmony_ci return self 23237db96d56Sopenharmony_ci 23247db96d56Sopenharmony_ci def __getinitargs__(self): 23257db96d56Sopenharmony_ci """pickle support""" 23267db96d56Sopenharmony_ci if self._name is None: 23277db96d56Sopenharmony_ci return (self._offset,) 23287db96d56Sopenharmony_ci return (self._offset, self._name) 23297db96d56Sopenharmony_ci 23307db96d56Sopenharmony_ci def __eq__(self, other): 23317db96d56Sopenharmony_ci if isinstance(other, timezone): 23327db96d56Sopenharmony_ci return self._offset == other._offset 23337db96d56Sopenharmony_ci return NotImplemented 23347db96d56Sopenharmony_ci 23357db96d56Sopenharmony_ci def __hash__(self): 23367db96d56Sopenharmony_ci return hash(self._offset) 23377db96d56Sopenharmony_ci 23387db96d56Sopenharmony_ci def __repr__(self): 23397db96d56Sopenharmony_ci """Convert to formal string, for repr(). 23407db96d56Sopenharmony_ci 23417db96d56Sopenharmony_ci >>> tz = timezone.utc 23427db96d56Sopenharmony_ci >>> repr(tz) 23437db96d56Sopenharmony_ci 'datetime.timezone.utc' 23447db96d56Sopenharmony_ci >>> tz = timezone(timedelta(hours=-5), 'EST') 23457db96d56Sopenharmony_ci >>> repr(tz) 23467db96d56Sopenharmony_ci "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')" 23477db96d56Sopenharmony_ci """ 23487db96d56Sopenharmony_ci if self is self.utc: 23497db96d56Sopenharmony_ci return 'datetime.timezone.utc' 23507db96d56Sopenharmony_ci if self._name is None: 23517db96d56Sopenharmony_ci return "%s.%s(%r)" % (self.__class__.__module__, 23527db96d56Sopenharmony_ci self.__class__.__qualname__, 23537db96d56Sopenharmony_ci self._offset) 23547db96d56Sopenharmony_ci return "%s.%s(%r, %r)" % (self.__class__.__module__, 23557db96d56Sopenharmony_ci self.__class__.__qualname__, 23567db96d56Sopenharmony_ci self._offset, self._name) 23577db96d56Sopenharmony_ci 23587db96d56Sopenharmony_ci def __str__(self): 23597db96d56Sopenharmony_ci return self.tzname(None) 23607db96d56Sopenharmony_ci 23617db96d56Sopenharmony_ci def utcoffset(self, dt): 23627db96d56Sopenharmony_ci if isinstance(dt, datetime) or dt is None: 23637db96d56Sopenharmony_ci return self._offset 23647db96d56Sopenharmony_ci raise TypeError("utcoffset() argument must be a datetime instance" 23657db96d56Sopenharmony_ci " or None") 23667db96d56Sopenharmony_ci 23677db96d56Sopenharmony_ci def tzname(self, dt): 23687db96d56Sopenharmony_ci if isinstance(dt, datetime) or dt is None: 23697db96d56Sopenharmony_ci if self._name is None: 23707db96d56Sopenharmony_ci return self._name_from_offset(self._offset) 23717db96d56Sopenharmony_ci return self._name 23727db96d56Sopenharmony_ci raise TypeError("tzname() argument must be a datetime instance" 23737db96d56Sopenharmony_ci " or None") 23747db96d56Sopenharmony_ci 23757db96d56Sopenharmony_ci def dst(self, dt): 23767db96d56Sopenharmony_ci if isinstance(dt, datetime) or dt is None: 23777db96d56Sopenharmony_ci return None 23787db96d56Sopenharmony_ci raise TypeError("dst() argument must be a datetime instance" 23797db96d56Sopenharmony_ci " or None") 23807db96d56Sopenharmony_ci 23817db96d56Sopenharmony_ci def fromutc(self, dt): 23827db96d56Sopenharmony_ci if isinstance(dt, datetime): 23837db96d56Sopenharmony_ci if dt.tzinfo is not self: 23847db96d56Sopenharmony_ci raise ValueError("fromutc: dt.tzinfo " 23857db96d56Sopenharmony_ci "is not self") 23867db96d56Sopenharmony_ci return dt + self._offset 23877db96d56Sopenharmony_ci raise TypeError("fromutc() argument must be a datetime instance" 23887db96d56Sopenharmony_ci " or None") 23897db96d56Sopenharmony_ci 23907db96d56Sopenharmony_ci _maxoffset = timedelta(hours=24, microseconds=-1) 23917db96d56Sopenharmony_ci _minoffset = -_maxoffset 23927db96d56Sopenharmony_ci 23937db96d56Sopenharmony_ci @staticmethod 23947db96d56Sopenharmony_ci def _name_from_offset(delta): 23957db96d56Sopenharmony_ci if not delta: 23967db96d56Sopenharmony_ci return 'UTC' 23977db96d56Sopenharmony_ci if delta < timedelta(0): 23987db96d56Sopenharmony_ci sign = '-' 23997db96d56Sopenharmony_ci delta = -delta 24007db96d56Sopenharmony_ci else: 24017db96d56Sopenharmony_ci sign = '+' 24027db96d56Sopenharmony_ci hours, rest = divmod(delta, timedelta(hours=1)) 24037db96d56Sopenharmony_ci minutes, rest = divmod(rest, timedelta(minutes=1)) 24047db96d56Sopenharmony_ci seconds = rest.seconds 24057db96d56Sopenharmony_ci microseconds = rest.microseconds 24067db96d56Sopenharmony_ci if microseconds: 24077db96d56Sopenharmony_ci return (f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}' 24087db96d56Sopenharmony_ci f'.{microseconds:06d}') 24097db96d56Sopenharmony_ci if seconds: 24107db96d56Sopenharmony_ci return f'UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}' 24117db96d56Sopenharmony_ci return f'UTC{sign}{hours:02d}:{minutes:02d}' 24127db96d56Sopenharmony_ci 24137db96d56Sopenharmony_ciUTC = timezone.utc = timezone._create(timedelta(0)) 24147db96d56Sopenharmony_ci 24157db96d56Sopenharmony_ci# bpo-37642: These attributes are rounded to the nearest minute for backwards 24167db96d56Sopenharmony_ci# compatibility, even though the constructor will accept a wider range of 24177db96d56Sopenharmony_ci# values. This may change in the future. 24187db96d56Sopenharmony_citimezone.min = timezone._create(-timedelta(hours=23, minutes=59)) 24197db96d56Sopenharmony_citimezone.max = timezone._create(timedelta(hours=23, minutes=59)) 24207db96d56Sopenharmony_ci_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) 24217db96d56Sopenharmony_ci 24227db96d56Sopenharmony_ci# Some time zone algebra. For a datetime x, let 24237db96d56Sopenharmony_ci# x.n = x stripped of its timezone -- its naive time. 24247db96d56Sopenharmony_ci# x.o = x.utcoffset(), and assuming that doesn't raise an exception or 24257db96d56Sopenharmony_ci# return None 24267db96d56Sopenharmony_ci# x.d = x.dst(), and assuming that doesn't raise an exception or 24277db96d56Sopenharmony_ci# return None 24287db96d56Sopenharmony_ci# x.s = x's standard offset, x.o - x.d 24297db96d56Sopenharmony_ci# 24307db96d56Sopenharmony_ci# Now some derived rules, where k is a duration (timedelta). 24317db96d56Sopenharmony_ci# 24327db96d56Sopenharmony_ci# 1. x.o = x.s + x.d 24337db96d56Sopenharmony_ci# This follows from the definition of x.s. 24347db96d56Sopenharmony_ci# 24357db96d56Sopenharmony_ci# 2. If x and y have the same tzinfo member, x.s = y.s. 24367db96d56Sopenharmony_ci# This is actually a requirement, an assumption we need to make about 24377db96d56Sopenharmony_ci# sane tzinfo classes. 24387db96d56Sopenharmony_ci# 24397db96d56Sopenharmony_ci# 3. The naive UTC time corresponding to x is x.n - x.o. 24407db96d56Sopenharmony_ci# This is again a requirement for a sane tzinfo class. 24417db96d56Sopenharmony_ci# 24427db96d56Sopenharmony_ci# 4. (x+k).s = x.s 24437db96d56Sopenharmony_ci# This follows from #2, and that datetime.timetz+timedelta preserves tzinfo. 24447db96d56Sopenharmony_ci# 24457db96d56Sopenharmony_ci# 5. (x+k).n = x.n + k 24467db96d56Sopenharmony_ci# Again follows from how arithmetic is defined. 24477db96d56Sopenharmony_ci# 24487db96d56Sopenharmony_ci# Now we can explain tz.fromutc(x). Let's assume it's an interesting case 24497db96d56Sopenharmony_ci# (meaning that the various tzinfo methods exist, and don't blow up or return 24507db96d56Sopenharmony_ci# None when called). 24517db96d56Sopenharmony_ci# 24527db96d56Sopenharmony_ci# The function wants to return a datetime y with timezone tz, equivalent to x. 24537db96d56Sopenharmony_ci# x is already in UTC. 24547db96d56Sopenharmony_ci# 24557db96d56Sopenharmony_ci# By #3, we want 24567db96d56Sopenharmony_ci# 24577db96d56Sopenharmony_ci# y.n - y.o = x.n [1] 24587db96d56Sopenharmony_ci# 24597db96d56Sopenharmony_ci# The algorithm starts by attaching tz to x.n, and calling that y. So 24607db96d56Sopenharmony_ci# x.n = y.n at the start. Then it wants to add a duration k to y, so that [1] 24617db96d56Sopenharmony_ci# becomes true; in effect, we want to solve [2] for k: 24627db96d56Sopenharmony_ci# 24637db96d56Sopenharmony_ci# (y+k).n - (y+k).o = x.n [2] 24647db96d56Sopenharmony_ci# 24657db96d56Sopenharmony_ci# By #1, this is the same as 24667db96d56Sopenharmony_ci# 24677db96d56Sopenharmony_ci# (y+k).n - ((y+k).s + (y+k).d) = x.n [3] 24687db96d56Sopenharmony_ci# 24697db96d56Sopenharmony_ci# By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start. 24707db96d56Sopenharmony_ci# Substituting that into [3], 24717db96d56Sopenharmony_ci# 24727db96d56Sopenharmony_ci# x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving 24737db96d56Sopenharmony_ci# k - (y+k).s - (y+k).d = 0; rearranging, 24747db96d56Sopenharmony_ci# k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so 24757db96d56Sopenharmony_ci# k = y.s - (y+k).d 24767db96d56Sopenharmony_ci# 24777db96d56Sopenharmony_ci# On the RHS, (y+k).d can't be computed directly, but y.s can be, and we 24787db96d56Sopenharmony_ci# approximate k by ignoring the (y+k).d term at first. Note that k can't be 24797db96d56Sopenharmony_ci# very large, since all offset-returning methods return a duration of magnitude 24807db96d56Sopenharmony_ci# less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must 24817db96d56Sopenharmony_ci# be 0, so ignoring it has no consequence then. 24827db96d56Sopenharmony_ci# 24837db96d56Sopenharmony_ci# In any case, the new value is 24847db96d56Sopenharmony_ci# 24857db96d56Sopenharmony_ci# z = y + y.s [4] 24867db96d56Sopenharmony_ci# 24877db96d56Sopenharmony_ci# It's helpful to step back at look at [4] from a higher level: it's simply 24887db96d56Sopenharmony_ci# mapping from UTC to tz's standard time. 24897db96d56Sopenharmony_ci# 24907db96d56Sopenharmony_ci# At this point, if 24917db96d56Sopenharmony_ci# 24927db96d56Sopenharmony_ci# z.n - z.o = x.n [5] 24937db96d56Sopenharmony_ci# 24947db96d56Sopenharmony_ci# we have an equivalent time, and are almost done. The insecurity here is 24957db96d56Sopenharmony_ci# at the start of daylight time. Picture US Eastern for concreteness. The wall 24967db96d56Sopenharmony_ci# time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good 24977db96d56Sopenharmony_ci# sense then. The docs ask that an Eastern tzinfo class consider such a time to 24987db96d56Sopenharmony_ci# be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST 24997db96d56Sopenharmony_ci# on the day DST starts. We want to return the 1:MM EST spelling because that's 25007db96d56Sopenharmony_ci# the only spelling that makes sense on the local wall clock. 25017db96d56Sopenharmony_ci# 25027db96d56Sopenharmony_ci# In fact, if [5] holds at this point, we do have the standard-time spelling, 25037db96d56Sopenharmony_ci# but that takes a bit of proof. We first prove a stronger result. What's the 25047db96d56Sopenharmony_ci# difference between the LHS and RHS of [5]? Let 25057db96d56Sopenharmony_ci# 25067db96d56Sopenharmony_ci# diff = x.n - (z.n - z.o) [6] 25077db96d56Sopenharmony_ci# 25087db96d56Sopenharmony_ci# Now 25097db96d56Sopenharmony_ci# z.n = by [4] 25107db96d56Sopenharmony_ci# (y + y.s).n = by #5 25117db96d56Sopenharmony_ci# y.n + y.s = since y.n = x.n 25127db96d56Sopenharmony_ci# x.n + y.s = since z and y are have the same tzinfo member, 25137db96d56Sopenharmony_ci# y.s = z.s by #2 25147db96d56Sopenharmony_ci# x.n + z.s 25157db96d56Sopenharmony_ci# 25167db96d56Sopenharmony_ci# Plugging that back into [6] gives 25177db96d56Sopenharmony_ci# 25187db96d56Sopenharmony_ci# diff = 25197db96d56Sopenharmony_ci# x.n - ((x.n + z.s) - z.o) = expanding 25207db96d56Sopenharmony_ci# x.n - x.n - z.s + z.o = cancelling 25217db96d56Sopenharmony_ci# - z.s + z.o = by #2 25227db96d56Sopenharmony_ci# z.d 25237db96d56Sopenharmony_ci# 25247db96d56Sopenharmony_ci# So diff = z.d. 25257db96d56Sopenharmony_ci# 25267db96d56Sopenharmony_ci# If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time 25277db96d56Sopenharmony_ci# spelling we wanted in the endcase described above. We're done. Contrarily, 25287db96d56Sopenharmony_ci# if z.d = 0, then we have a UTC equivalent, and are also done. 25297db96d56Sopenharmony_ci# 25307db96d56Sopenharmony_ci# If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to 25317db96d56Sopenharmony_ci# add to z (in effect, z is in tz's standard time, and we need to shift the 25327db96d56Sopenharmony_ci# local clock into tz's daylight time). 25337db96d56Sopenharmony_ci# 25347db96d56Sopenharmony_ci# Let 25357db96d56Sopenharmony_ci# 25367db96d56Sopenharmony_ci# z' = z + z.d = z + diff [7] 25377db96d56Sopenharmony_ci# 25387db96d56Sopenharmony_ci# and we can again ask whether 25397db96d56Sopenharmony_ci# 25407db96d56Sopenharmony_ci# z'.n - z'.o = x.n [8] 25417db96d56Sopenharmony_ci# 25427db96d56Sopenharmony_ci# If so, we're done. If not, the tzinfo class is insane, according to the 25437db96d56Sopenharmony_ci# assumptions we've made. This also requires a bit of proof. As before, let's 25447db96d56Sopenharmony_ci# compute the difference between the LHS and RHS of [8] (and skipping some of 25457db96d56Sopenharmony_ci# the justifications for the kinds of substitutions we've done several times 25467db96d56Sopenharmony_ci# already): 25477db96d56Sopenharmony_ci# 25487db96d56Sopenharmony_ci# diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7] 25497db96d56Sopenharmony_ci# x.n - (z.n + diff - z'.o) = replacing diff via [6] 25507db96d56Sopenharmony_ci# x.n - (z.n + x.n - (z.n - z.o) - z'.o) = 25517db96d56Sopenharmony_ci# x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n 25527db96d56Sopenharmony_ci# - z.n + z.n - z.o + z'.o = cancel z.n 25537db96d56Sopenharmony_ci# - z.o + z'.o = #1 twice 25547db96d56Sopenharmony_ci# -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo 25557db96d56Sopenharmony_ci# z'.d - z.d 25567db96d56Sopenharmony_ci# 25577db96d56Sopenharmony_ci# So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal, 25587db96d56Sopenharmony_ci# we've found the UTC-equivalent so are done. In fact, we stop with [7] and 25597db96d56Sopenharmony_ci# return z', not bothering to compute z'.d. 25607db96d56Sopenharmony_ci# 25617db96d56Sopenharmony_ci# How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by 25627db96d56Sopenharmony_ci# a dst() offset, and starting *from* a time already in DST (we know z.d != 0), 25637db96d56Sopenharmony_ci# would have to change the result dst() returns: we start in DST, and moving 25647db96d56Sopenharmony_ci# a little further into it takes us out of DST. 25657db96d56Sopenharmony_ci# 25667db96d56Sopenharmony_ci# There isn't a sane case where this can happen. The closest it gets is at 25677db96d56Sopenharmony_ci# the end of DST, where there's an hour in UTC with no spelling in a hybrid 25687db96d56Sopenharmony_ci# tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During 25697db96d56Sopenharmony_ci# that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM 25707db96d56Sopenharmony_ci# UTC) because the docs insist on that, but 0:MM is taken as being in daylight 25717db96d56Sopenharmony_ci# time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local 25727db96d56Sopenharmony_ci# clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in 25737db96d56Sopenharmony_ci# standard time. Since that's what the local clock *does*, we want to map both 25747db96d56Sopenharmony_ci# UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous 25757db96d56Sopenharmony_ci# in local time, but so it goes -- it's the way the local clock works. 25767db96d56Sopenharmony_ci# 25777db96d56Sopenharmony_ci# When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0, 25787db96d56Sopenharmony_ci# so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going. 25797db96d56Sopenharmony_ci# z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8] 25807db96d56Sopenharmony_ci# (correctly) concludes that z' is not UTC-equivalent to x. 25817db96d56Sopenharmony_ci# 25827db96d56Sopenharmony_ci# Because we know z.d said z was in daylight time (else [5] would have held and 25837db96d56Sopenharmony_ci# we would have stopped then), and we know z.d != z'.d (else [8] would have held 25847db96d56Sopenharmony_ci# and we have stopped then), and there are only 2 possible values dst() can 25857db96d56Sopenharmony_ci# return in Eastern, it follows that z'.d must be 0 (which it is in the example, 25867db96d56Sopenharmony_ci# but the reasoning doesn't depend on the example -- it depends on there being 25877db96d56Sopenharmony_ci# two possible dst() outcomes, one zero and the other non-zero). Therefore 25887db96d56Sopenharmony_ci# z' must be in standard time, and is the spelling we want in this case. 25897db96d56Sopenharmony_ci# 25907db96d56Sopenharmony_ci# Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is 25917db96d56Sopenharmony_ci# concerned (because it takes z' as being in standard time rather than the 25927db96d56Sopenharmony_ci# daylight time we intend here), but returning it gives the real-life "local 25937db96d56Sopenharmony_ci# clock repeats an hour" behavior when mapping the "unspellable" UTC hour into 25947db96d56Sopenharmony_ci# tz. 25957db96d56Sopenharmony_ci# 25967db96d56Sopenharmony_ci# When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with 25977db96d56Sopenharmony_ci# the 1:MM standard time spelling we want. 25987db96d56Sopenharmony_ci# 25997db96d56Sopenharmony_ci# So how can this break? One of the assumptions must be violated. Two 26007db96d56Sopenharmony_ci# possibilities: 26017db96d56Sopenharmony_ci# 26027db96d56Sopenharmony_ci# 1) [2] effectively says that y.s is invariant across all y belong to a given 26037db96d56Sopenharmony_ci# time zone. This isn't true if, for political reasons or continental drift, 26047db96d56Sopenharmony_ci# a region decides to change its base offset from UTC. 26057db96d56Sopenharmony_ci# 26067db96d56Sopenharmony_ci# 2) There may be versions of "double daylight" time where the tail end of 26077db96d56Sopenharmony_ci# the analysis gives up a step too early. I haven't thought about that 26087db96d56Sopenharmony_ci# enough to say. 26097db96d56Sopenharmony_ci# 26107db96d56Sopenharmony_ci# In any case, it's clear that the default fromutc() is strong enough to handle 26117db96d56Sopenharmony_ci# "almost all" time zones: so long as the standard offset is invariant, it 26127db96d56Sopenharmony_ci# doesn't matter if daylight time transition points change from year to year, or 26137db96d56Sopenharmony_ci# if daylight time is skipped in some years; it doesn't matter how large or 26147db96d56Sopenharmony_ci# small dst() may get within its bounds; and it doesn't even matter if some 26157db96d56Sopenharmony_ci# perverse time zone returns a negative dst()). So a breaking case must be 26167db96d56Sopenharmony_ci# pretty bizarre, and a tzinfo subclass can override fromutc() if it is. 26177db96d56Sopenharmony_ci 26187db96d56Sopenharmony_citry: 26197db96d56Sopenharmony_ci from _datetime import * 26207db96d56Sopenharmony_ciexcept ImportError: 26217db96d56Sopenharmony_ci pass 26227db96d56Sopenharmony_cielse: 26237db96d56Sopenharmony_ci # Clean up unused names 26247db96d56Sopenharmony_ci del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, _DI100Y, _DI400Y, 26257db96d56Sopenharmony_ci _DI4Y, _EPOCH, _MAXORDINAL, _MONTHNAMES, _build_struct_time, 26267db96d56Sopenharmony_ci _check_date_fields, _check_time_fields, 26277db96d56Sopenharmony_ci _check_tzinfo_arg, _check_tzname, _check_utc_offset, _cmp, _cmperror, 26287db96d56Sopenharmony_ci _date_class, _days_before_month, _days_before_year, _days_in_month, 26297db96d56Sopenharmony_ci _format_time, _format_offset, _index, _is_leap, _isoweek1monday, _math, 26307db96d56Sopenharmony_ci _ord2ymd, _time, _time_class, _tzinfo_class, _wrap_strftime, _ymd2ord, 26317db96d56Sopenharmony_ci _divide_and_round, _parse_isoformat_date, _parse_isoformat_time, 26327db96d56Sopenharmony_ci _parse_hh_mm_ss_ff, _IsoCalendarDate, _isoweek_to_gregorian, 26337db96d56Sopenharmony_ci _find_isoformat_datetime_separator, _FRACTION_CORRECTION, 26347db96d56Sopenharmony_ci _is_ascii_digit) 26357db96d56Sopenharmony_ci # XXX Since import * above excludes names that start with _, 26367db96d56Sopenharmony_ci # docstring does not get overwritten. In the future, it may be 26377db96d56Sopenharmony_ci # appropriate to maintain a single module level docstring and 26387db96d56Sopenharmony_ci # remove the following line. 26397db96d56Sopenharmony_ci from _datetime import __doc__ 2640