• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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