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