168 lines
5.2 KiB
Python
168 lines
5.2 KiB
Python
import re
|
|
import sys
|
|
|
|
try:
|
|
from datetime import timezone
|
|
except ImportError:
|
|
from datetime import datetime
|
|
from datetime import timedelta
|
|
from datetime import tzinfo
|
|
|
|
class timezone(tzinfo):
|
|
__slots__ = "_offset", "_name"
|
|
|
|
# Sentinel value to disallow None
|
|
_Omitted = object()
|
|
|
|
def __new__(cls, offset, name=_Omitted):
|
|
if not isinstance(offset, timedelta):
|
|
raise TypeError("offset must be a timedelta")
|
|
if name is cls._Omitted:
|
|
if not offset:
|
|
return cls.utc
|
|
name = None
|
|
elif not isinstance(name, str):
|
|
raise TypeError("name must be a string")
|
|
if not cls._minoffset <= offset <= cls._maxoffset:
|
|
raise ValueError(
|
|
"offset must be a timedelta "
|
|
"strictly between -timedelta(hours=24) and "
|
|
"timedelta(hours=24)."
|
|
)
|
|
return cls._create(offset, name)
|
|
|
|
@classmethod
|
|
def _create(cls, offset, name=None):
|
|
self = tzinfo.__new__(cls)
|
|
self._offset = offset
|
|
self._name = name
|
|
return self
|
|
|
|
def __getinitargs__(self):
|
|
"""pickle support"""
|
|
if self._name is None:
|
|
return (self._offset,)
|
|
return (self._offset, self._name)
|
|
|
|
def __eq__(self, other):
|
|
if type(other) != timezone:
|
|
return False
|
|
return self._offset == other._offset
|
|
|
|
def __hash__(self):
|
|
return hash(self._offset)
|
|
|
|
def __repr__(self):
|
|
"""Convert to formal string, for repr().
|
|
|
|
>>> tz = timezone.utc
|
|
>>> repr(tz)
|
|
'datetime.timezone.utc'
|
|
>>> tz = timezone(timedelta(hours=-5), 'EST')
|
|
>>> repr(tz)
|
|
"datetime.timezone(datetime.timedelta(-1, 68400), 'EST')"
|
|
"""
|
|
if self is self.utc:
|
|
return "datetime.timezone.utc"
|
|
if self._name is None:
|
|
return "%s.%s(%r)" % (
|
|
self.__class__.__module__,
|
|
self.__class__.__name__,
|
|
self._offset,
|
|
)
|
|
return "%s.%s(%r, %r)" % (
|
|
self.__class__.__module__,
|
|
self.__class__.__name__,
|
|
self._offset,
|
|
self._name,
|
|
)
|
|
|
|
def __str__(self):
|
|
return self.tzname(None)
|
|
|
|
def utcoffset(self, dt):
|
|
if isinstance(dt, datetime) or dt is None:
|
|
return self._offset
|
|
raise TypeError(
|
|
"utcoffset() argument must be a datetime instance" " or None"
|
|
)
|
|
|
|
def tzname(self, dt):
|
|
if isinstance(dt, datetime) or dt is None:
|
|
if self._name is None:
|
|
return self._name_from_offset(self._offset)
|
|
return self._name
|
|
raise TypeError("tzname() argument must be a datetime instance" " or None")
|
|
|
|
def dst(self, dt):
|
|
if isinstance(dt, datetime) or dt is None:
|
|
return None
|
|
raise TypeError("dst() argument must be a datetime instance" " or None")
|
|
|
|
def fromutc(self, dt):
|
|
if isinstance(dt, datetime):
|
|
if dt.tzinfo is not self:
|
|
raise ValueError("fromutc: dt.tzinfo " "is not self")
|
|
return dt + self._offset
|
|
raise TypeError("fromutc() argument must be a datetime instance" " or None")
|
|
|
|
_maxoffset = timedelta(hours=23, minutes=59)
|
|
_minoffset = -_maxoffset
|
|
|
|
@staticmethod
|
|
def _name_from_offset(delta):
|
|
if not delta:
|
|
return "UTC"
|
|
if delta < timedelta(0):
|
|
sign = "-"
|
|
delta = -delta
|
|
else:
|
|
sign = "+"
|
|
hours, rest = divmod(delta, timedelta(hours=1))
|
|
minutes, rest = divmod(rest, timedelta(minutes=1))
|
|
seconds = rest.seconds
|
|
microseconds = rest.microseconds
|
|
if microseconds:
|
|
return ("UTC{}{:02d}:{:02d}:{:02d}.{:06d}").format(
|
|
sign, hours, minutes, seconds, microseconds
|
|
)
|
|
if seconds:
|
|
return "UTC{}{:02d}:{:02d}:{:02d}".format(sign, hours, minutes, seconds)
|
|
return "UTC{}{:02d}:{:02d}".format(sign, hours, minutes)
|
|
|
|
timezone.utc = timezone._create(timedelta(0))
|
|
timezone.min = timezone._create(timezone._minoffset)
|
|
timezone.max = timezone._create(timezone._maxoffset)
|
|
|
|
|
|
PY2 = sys.version_info[0] == 2
|
|
PY36 = sys.version_info >= (3, 6)
|
|
PY38 = sys.version_info >= (3, 8)
|
|
|
|
if PY2:
|
|
unicode = unicode
|
|
chr = unichr
|
|
long = long
|
|
else:
|
|
unicode = str
|
|
chr = chr
|
|
long = int
|
|
|
|
|
|
def decode(string, encodings=None):
|
|
if not PY2 and not isinstance(string, bytes):
|
|
return string
|
|
|
|
if PY2 and isinstance(string, unicode):
|
|
return string
|
|
|
|
encodings = encodings or ["utf-8", "latin1", "ascii"]
|
|
|
|
for encoding in encodings:
|
|
try:
|
|
return string.decode(encoding)
|
|
except (UnicodeEncodeError, UnicodeDecodeError):
|
|
pass
|
|
|
|
return string.decode(encodings[0], errors="ignore")
|