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