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