kopia lustrzana https://github.com/micropython/micropython-lib
python-stdlib/datetime: Add new implementation of datetime module.
This new module is a port of Python datetime providing classes for manipulating dates, times, and deltas. It completely replaces the existing unix-ffi version. Signed-off-by: Lorenzo Cappelletti <lorenzo.cappelletti@gmail.com>pull/493/head
rodzic
64b8817c0d
commit
fc86070ffb
|
@ -0,0 +1,879 @@
|
|||
# datetime.py
|
||||
|
||||
import time as _tmod
|
||||
|
||||
__version__ = "2.0.0"
|
||||
|
||||
_DBM = (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
|
||||
_DIM = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
|
||||
_TIME_SPEC = ("auto", "hours", "minutes", "seconds", "milliseconds", "microseconds")
|
||||
|
||||
|
||||
def _leap(y):
|
||||
return y % 4 == 0 and (y % 100 != 0 or y % 400 == 0)
|
||||
|
||||
|
||||
def _dby(y):
|
||||
# year -> number of days before January 1st of year.
|
||||
Y = y - 1
|
||||
return Y * 365 + Y // 4 - Y // 100 + Y // 400
|
||||
|
||||
|
||||
def _dim(y, m):
|
||||
# year, month -> number of days in that month in that year.
|
||||
if m == 2 and _leap(y):
|
||||
return 29
|
||||
return _DIM[m]
|
||||
|
||||
|
||||
def _dbm(y, m):
|
||||
# year, month -> number of days in year preceding first day of month.
|
||||
return _DBM[m] + (m > 2 and _leap(y))
|
||||
|
||||
|
||||
def _ymd2o(y, m, d):
|
||||
# y, month, day -> ordinal, considering 01-Jan-0001 as day 1.
|
||||
return _dby(y) + _dbm(y, m) + d
|
||||
|
||||
|
||||
def _o2ymd(n):
|
||||
# ordinal -> (year, month, day), considering 01-Jan-0001 as day 1.
|
||||
n -= 1
|
||||
n400, n = divmod(n, 146_097)
|
||||
y = n400 * 400 + 1
|
||||
n100, n = divmod(n, 36_524)
|
||||
n4, n = divmod(n, 1_461)
|
||||
n1, n = divmod(n, 365)
|
||||
y += n100 * 100 + n4 * 4 + n1
|
||||
if n1 == 4 or n100 == 4:
|
||||
return y - 1, 12, 31
|
||||
m = (n + 50) >> 5
|
||||
prec = _dbm(y, m)
|
||||
if prec > n:
|
||||
m -= 1
|
||||
prec -= _dim(y, m)
|
||||
n -= prec
|
||||
return y, m, n + 1
|
||||
|
||||
|
||||
MINYEAR = 1
|
||||
MAXYEAR = 9_999
|
||||
|
||||
|
||||
class timedelta:
|
||||
def __init__(
|
||||
self, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0
|
||||
):
|
||||
s = (((weeks * 7 + days) * 24 + hours) * 60 + minutes) * 60 + seconds
|
||||
self._us = round((s * 1000 + milliseconds) * 1000 + microseconds)
|
||||
|
||||
def __repr__(self):
|
||||
return "datetime.timedelta(microseconds={})".format(self._us)
|
||||
|
||||
def total_seconds(self):
|
||||
return self._us / 1_000_000
|
||||
|
||||
@property
|
||||
def days(self):
|
||||
return self._tuple(2)[0]
|
||||
|
||||
@property
|
||||
def seconds(self):
|
||||
return self._tuple(3)[1]
|
||||
|
||||
@property
|
||||
def microseconds(self):
|
||||
return self._tuple(3)[2]
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, datetime):
|
||||
return other.__add__(self)
|
||||
else:
|
||||
us = other._us
|
||||
return timedelta(0, 0, self._us + us)
|
||||
|
||||
def __sub__(self, other):
|
||||
return timedelta(0, 0, self._us - other._us)
|
||||
|
||||
def __neg__(self):
|
||||
return timedelta(0, 0, -self._us)
|
||||
|
||||
def __pos__(self):
|
||||
return self
|
||||
|
||||
def __abs__(self):
|
||||
return -self if self._us < 0 else self
|
||||
|
||||
def __mul__(self, other):
|
||||
return timedelta(0, 0, round(other * self._us))
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __truediv__(self, other):
|
||||
if isinstance(other, timedelta):
|
||||
return self._us / other._us
|
||||
else:
|
||||
return timedelta(0, 0, round(self._us / other))
|
||||
|
||||
def __floordiv__(self, other):
|
||||
if isinstance(other, timedelta):
|
||||
return self._us // other._us
|
||||
else:
|
||||
return timedelta(0, 0, int(self._us // other))
|
||||
|
||||
def __mod__(self, other):
|
||||
return timedelta(0, 0, self._us % other._us)
|
||||
|
||||
def __divmod__(self, other):
|
||||
q, r = divmod(self._us, other._us)
|
||||
return q, timedelta(0, 0, r)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._us == other._us
|
||||
|
||||
def __le__(self, other):
|
||||
return self._us <= other._us
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._us < other._us
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._us >= other._us
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._us > other._us
|
||||
|
||||
def __bool__(self):
|
||||
return self._us != 0
|
||||
|
||||
def __str__(self):
|
||||
return self._format(0x40)
|
||||
|
||||
def __hash__(self):
|
||||
if not hasattr(self, "_hash"):
|
||||
self._hash = hash(self._us)
|
||||
return self._hash
|
||||
|
||||
def isoformat(self):
|
||||
return self._format(0)
|
||||
|
||||
def _format(self, spec=0):
|
||||
if self._us >= 0:
|
||||
td = self
|
||||
g = ""
|
||||
else:
|
||||
td = -self
|
||||
g = "-"
|
||||
d, h, m, s, us = td._tuple(5)
|
||||
ms, us = divmod(us, 1000)
|
||||
r = ""
|
||||
if spec & 0x40:
|
||||
spec &= ~0x40
|
||||
hr = str(h)
|
||||
else:
|
||||
hr = f"{h:02d}"
|
||||
if spec & 0x20:
|
||||
spec &= ~0x20
|
||||
spec |= 0x10
|
||||
r += "UTC"
|
||||
if spec & 0x10:
|
||||
spec &= ~0x10
|
||||
if not g:
|
||||
g = "+"
|
||||
if d:
|
||||
p = "s" if d > 1 else ""
|
||||
r += f"{g}{d} day{p}, "
|
||||
g = ""
|
||||
if spec == 0:
|
||||
spec = 5 if (ms or us) else 3
|
||||
if spec >= 1 or h:
|
||||
r += f"{g}{hr}"
|
||||
if spec >= 2 or m:
|
||||
r += f":{m:02d}"
|
||||
if spec >= 3 or s:
|
||||
r += f":{s:02d}"
|
||||
if spec >= 4 or ms:
|
||||
r += f".{ms:03d}"
|
||||
if spec >= 5 or us:
|
||||
r += f"{us:03d}"
|
||||
return r
|
||||
|
||||
def tuple(self):
|
||||
return self._tuple(5)
|
||||
|
||||
def _tuple(self, n):
|
||||
d, us = divmod(self._us, 86_400_000_000)
|
||||
if n == 2:
|
||||
return d, us
|
||||
s, us = divmod(us, 1_000_000)
|
||||
if n == 3:
|
||||
return d, s, us
|
||||
h, s = divmod(s, 3600)
|
||||
m, s = divmod(s, 60)
|
||||
return d, h, m, s, us
|
||||
|
||||
|
||||
timedelta.min = timedelta(days=-999_999_999)
|
||||
timedelta.max = timedelta(days=999_999_999, hours=23, minutes=59, seconds=59, microseconds=999_999)
|
||||
timedelta.resolution = timedelta(microseconds=1)
|
||||
|
||||
|
||||
class tzinfo:
|
||||
# abstract class
|
||||
def tzname(self, dt):
|
||||
raise NotImplementedError
|
||||
|
||||
def utcoffset(self, dt):
|
||||
raise NotImplementedError
|
||||
|
||||
def dst(self, dt):
|
||||
raise NotImplementedError
|
||||
|
||||
def fromutc(self, dt):
|
||||
if dt._tz is not self:
|
||||
raise ValueError
|
||||
|
||||
# See original datetime.py for an explanation of this algorithm.
|
||||
dtoff = dt.utcoffset()
|
||||
dtdst = dt.dst()
|
||||
delta = dtoff - dtdst
|
||||
if delta:
|
||||
dt += delta
|
||||
dtdst = dt.dst()
|
||||
return dt + dtdst
|
||||
|
||||
def isoformat(self, dt):
|
||||
return self.utcoffset(dt)._format(0x12)
|
||||
|
||||
|
||||
class timezone(tzinfo):
|
||||
def __init__(self, offset, name=None):
|
||||
if not (abs(offset._us) < 86_400_000_000):
|
||||
raise ValueError
|
||||
self._offset = offset
|
||||
self._name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "datetime.timezone({}, {})".format(repr(self._offset), repr(self._name))
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, timezone):
|
||||
return self._offset == other._offset
|
||||
return NotImplemented
|
||||
|
||||
def __str__(self):
|
||||
return self.tzname(None)
|
||||
|
||||
def __hash__(self):
|
||||
if not hasattr(self, "_hash"):
|
||||
self._hash = hash((self._offset, self._name))
|
||||
return self._hash
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self._offset
|
||||
|
||||
def dst(self, dt):
|
||||
return None
|
||||
|
||||
def tzname(self, dt):
|
||||
if self._name:
|
||||
return self._name
|
||||
return self._offset._format(0x22)
|
||||
|
||||
def fromutc(self, dt):
|
||||
return dt + self._offset
|
||||
|
||||
|
||||
timezone.utc = timezone(timedelta(0))
|
||||
|
||||
|
||||
def _date(y, m, d):
|
||||
if MINYEAR <= y <= MAXYEAR and 1 <= m <= 12 and 1 <= d <= _dim(y, m):
|
||||
return _ymd2o(y, m, d)
|
||||
elif y == 0 and m == 0 and 1 <= d <= 3_652_059:
|
||||
return d
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
|
||||
def _iso2d(s): # ISO -> date
|
||||
if len(s) < 10 or s[4] != "-" or s[7] != "-":
|
||||
raise ValueError
|
||||
return int(s[0:4]), int(s[5:7]), int(s[8:10])
|
||||
|
||||
|
||||
def _d2iso(o): # date -> ISO
|
||||
return "%04d-%02d-%02d" % _o2ymd(o)
|
||||
|
||||
|
||||
class date:
|
||||
def __init__(self, year, month, day):
|
||||
self._ord = _date(year, month, day)
|
||||
|
||||
@classmethod
|
||||
def fromtimestamp(cls, ts):
|
||||
return cls(*_tmod.localtime(ts)[:3])
|
||||
|
||||
@classmethod
|
||||
def today(cls):
|
||||
return cls(*_tmod.localtime()[:3])
|
||||
|
||||
@classmethod
|
||||
def fromordinal(cls, n):
|
||||
return cls(0, 0, n)
|
||||
|
||||
@classmethod
|
||||
def fromisoformat(cls, s):
|
||||
return cls(*_iso2d(s))
|
||||
|
||||
@property
|
||||
def year(self):
|
||||
return self.tuple()[0]
|
||||
|
||||
@property
|
||||
def month(self):
|
||||
return self.tuple()[1]
|
||||
|
||||
@property
|
||||
def day(self):
|
||||
return self.tuple()[2]
|
||||
|
||||
def toordinal(self):
|
||||
return self._ord
|
||||
|
||||
def timetuple(self):
|
||||
y, m, d = self.tuple()
|
||||
yday = _dbm(y, m) + d
|
||||
return (y, m, d, 0, 0, 0, self.weekday(), yday, -1)
|
||||
|
||||
def replace(self, year=None, month=None, day=None):
|
||||
year_, month_, day_ = self.tuple()
|
||||
if year is None:
|
||||
year = year_
|
||||
if month is None:
|
||||
month = month_
|
||||
if day is None:
|
||||
day = day_
|
||||
return date(year, month, day)
|
||||
|
||||
def __add__(self, other):
|
||||
return date.fromordinal(self._ord + other.days)
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, date):
|
||||
return timedelta(days=self._ord - other._ord)
|
||||
else:
|
||||
return date.fromordinal(self._ord - other.days)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, date):
|
||||
return self._ord == other._ord
|
||||
else:
|
||||
return False
|
||||
|
||||
def __le__(self, other):
|
||||
return self._ord <= other._ord
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._ord < other._ord
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._ord >= other._ord
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._ord > other._ord
|
||||
|
||||
def weekday(self):
|
||||
return (self._ord + 6) % 7
|
||||
|
||||
def isoweekday(self):
|
||||
return self._ord % 7 or 7
|
||||
|
||||
def isoformat(self):
|
||||
return _d2iso(self._ord)
|
||||
|
||||
def __repr__(self):
|
||||
return "datetime.date(0, 0, {})".format(self._ord)
|
||||
|
||||
__str__ = isoformat
|
||||
|
||||
def __hash__(self):
|
||||
if not hasattr(self, "_hash"):
|
||||
self._hash = hash(self._ord)
|
||||
return self._hash
|
||||
|
||||
def tuple(self):
|
||||
return _o2ymd(self._ord)
|
||||
|
||||
|
||||
date.min = date(MINYEAR, 1, 1)
|
||||
date.max = date(MAXYEAR, 12, 31)
|
||||
date.resolution = timedelta(days=1)
|
||||
|
||||
|
||||
def _time(h, m, s, us, fold):
|
||||
if (
|
||||
0 <= h < 24
|
||||
and 0 <= m < 60
|
||||
and 0 <= s < 60
|
||||
and 0 <= us < 1_000_000
|
||||
and (fold == 0 or fold == 1)
|
||||
) or (h == 0 and m == 0 and s == 0 and 0 < us < 86_400_000_000):
|
||||
return timedelta(0, s, us, 0, m, h)
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
|
||||
def _iso2t(s):
|
||||
hour = 0
|
||||
minute = 0
|
||||
sec = 0
|
||||
usec = 0
|
||||
tz_sign = ""
|
||||
tz_hour = 0
|
||||
tz_minute = 0
|
||||
tz_sec = 0
|
||||
tz_usec = 0
|
||||
l = len(s)
|
||||
i = 0
|
||||
if l < 2:
|
||||
raise ValueError
|
||||
i += 2
|
||||
hour = int(s[i - 2 : i])
|
||||
if l > i and s[i] == ":":
|
||||
i += 3
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
minute = int(s[i - 2 : i])
|
||||
if l > i and s[i] == ":":
|
||||
i += 3
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
sec = int(s[i - 2 : i])
|
||||
if l > i and s[i] == ".":
|
||||
i += 4
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
usec = 1000 * int(s[i - 3 : i])
|
||||
if l > i and s[i] != "+":
|
||||
i += 3
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
usec += int(s[i - 3 : i])
|
||||
if l > i:
|
||||
if s[i] not in "+-":
|
||||
raise ValueError
|
||||
tz_sign = s[i]
|
||||
i += 6
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
tz_hour = int(s[i - 5 : i - 3])
|
||||
tz_minute = int(s[i - 2 : i])
|
||||
if l > i and s[i] == ":":
|
||||
i += 3
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
tz_sec = int(s[i - 2 : i])
|
||||
if l > i and s[i] == ".":
|
||||
i += 7
|
||||
if l - i < 0:
|
||||
raise ValueError
|
||||
tz_usec = int(s[i - 6 : i])
|
||||
if l != i:
|
||||
raise ValueError
|
||||
if tz_sign:
|
||||
td = timedelta(hours=tz_hour, minutes=tz_minute, seconds=tz_sec, microseconds=tz_usec)
|
||||
if tz_sign == "-":
|
||||
td = -td
|
||||
tz = timezone(td)
|
||||
else:
|
||||
tz = None
|
||||
return hour, minute, sec, usec, tz
|
||||
|
||||
|
||||
def _t2iso(td, timespec, dt, tz):
|
||||
s = td._format(_TIME_SPEC.index(timespec))
|
||||
if tz is not None:
|
||||
s += tz.isoformat(dt)
|
||||
return s
|
||||
|
||||
|
||||
class time:
|
||||
def __init__(self, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0):
|
||||
self._td = _time(hour, minute, second, microsecond, fold)
|
||||
self._tz = tzinfo
|
||||
self._fd = fold
|
||||
|
||||
@classmethod
|
||||
def fromisoformat(cls, s):
|
||||
return cls(*_iso2t(s))
|
||||
|
||||
@property
|
||||
def hour(self):
|
||||
return self.tuple()[0]
|
||||
|
||||
@property
|
||||
def minute(self):
|
||||
return self.tuple()[1]
|
||||
|
||||
@property
|
||||
def second(self):
|
||||
return self.tuple()[2]
|
||||
|
||||
@property
|
||||
def microsecond(self):
|
||||
return self.tuple()[3]
|
||||
|
||||
@property
|
||||
def tzinfo(self):
|
||||
return self._tz
|
||||
|
||||
@property
|
||||
def fold(self):
|
||||
return self._fd
|
||||
|
||||
def replace(
|
||||
self, hour=None, minute=None, second=None, microsecond=None, tzinfo=True, *, fold=None
|
||||
):
|
||||
h, m, s, us, tz, fl = self.tuple()
|
||||
if hour is None:
|
||||
hour = h
|
||||
if minute is None:
|
||||
minute = m
|
||||
if second is None:
|
||||
second = s
|
||||
if microsecond is None:
|
||||
microsecond = us
|
||||
if tzinfo is True:
|
||||
tzinfo = tz
|
||||
if fold is None:
|
||||
fold = fl
|
||||
return time(hour, minute, second, microsecond, tzinfo, fold=fold)
|
||||
|
||||
def isoformat(self, timespec="auto"):
|
||||
return _t2iso(self._td, timespec, None, self._tz)
|
||||
|
||||
def __repr__(self):
|
||||
return "datetime.time(microsecond={}, tzinfo={}, fold={})".format(
|
||||
self._td._us, repr(self._tz), self._fd
|
||||
)
|
||||
|
||||
__str__ = isoformat
|
||||
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
def __eq__(self, other):
|
||||
if (self._tz == None) ^ (other._tz == None):
|
||||
return False
|
||||
return self._sub(other) == 0
|
||||
|
||||
def __le__(self, other):
|
||||
return self._sub(other) <= 0
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._sub(other) < 0
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._sub(other) >= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._sub(other) > 0
|
||||
|
||||
def _sub(self, other):
|
||||
tz1 = self._tz
|
||||
if (tz1 is None) ^ (other._tz is None):
|
||||
raise TypeError
|
||||
us1 = self._td._us
|
||||
us2 = other._td._us
|
||||
if tz1 is not None:
|
||||
os1 = self.utcoffset()._us
|
||||
os2 = other.utcoffset()._us
|
||||
if os1 != os2:
|
||||
us1 -= os1
|
||||
us2 -= os2
|
||||
return us1 - us2
|
||||
|
||||
def __hash__(self):
|
||||
if not hasattr(self, "_hash"):
|
||||
# fold doesn't make any difference
|
||||
self._hash = hash((self._td, self._tz))
|
||||
return self._hash
|
||||
|
||||
def utcoffset(self):
|
||||
return None if self._tz is None else self._tz.utcoffset(None)
|
||||
|
||||
def dst(self):
|
||||
return None if self._tz is None else self._tz.dst(None)
|
||||
|
||||
def tzname(self):
|
||||
return None if self._tz is None else self._tz.tzname(None)
|
||||
|
||||
def tuple(self):
|
||||
d, h, m, s, us = self._td.tuple()
|
||||
return h, m, s, us, self._tz, self._fd
|
||||
|
||||
|
||||
time.min = time(0)
|
||||
time.max = time(23, 59, 59, 999_999)
|
||||
time.resolution = timedelta.resolution
|
||||
|
||||
|
||||
class datetime:
|
||||
def __init__(
|
||||
self, year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold=0
|
||||
):
|
||||
self._d = _date(year, month, day)
|
||||
self._t = _time(hour, minute, second, microsecond, fold)
|
||||
self._tz = tzinfo
|
||||
self._fd = fold
|
||||
|
||||
@classmethod
|
||||
def fromtimestamp(cls, ts, tz=None):
|
||||
if isinstance(ts, float):
|
||||
ts, us = divmod(round(ts * 1_000_000), 1_000_000)
|
||||
else:
|
||||
us = 0
|
||||
if tz is None:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
dt = cls(*_tmod.gmtime(ts)[:6], microsecond=us, tzinfo=tz)
|
||||
dt = tz.fromutc(dt)
|
||||
return dt
|
||||
|
||||
@classmethod
|
||||
def now(cls, tz=None):
|
||||
return cls.fromtimestamp(_tmod.time(), tz)
|
||||
|
||||
@classmethod
|
||||
def fromordinal(cls, n):
|
||||
return cls(0, 0, n)
|
||||
|
||||
@classmethod
|
||||
def fromisoformat(cls, s):
|
||||
d = _iso2d(s)
|
||||
if len(s) <= 12:
|
||||
return cls(*d)
|
||||
t = _iso2t(s[11:])
|
||||
return cls(*(d + t))
|
||||
|
||||
@classmethod
|
||||
def combine(cls, date, time, tzinfo=None):
|
||||
return cls(
|
||||
0, 0, date.toordinal(), 0, 0, 0, time._td._us, tzinfo or time._tz, fold=time._fd
|
||||
)
|
||||
|
||||
@property
|
||||
def year(self):
|
||||
return _o2ymd(self._d)[0]
|
||||
|
||||
@property
|
||||
def month(self):
|
||||
return _o2ymd(self._d)[1]
|
||||
|
||||
@property
|
||||
def day(self):
|
||||
return _o2ymd(self._d)[2]
|
||||
|
||||
@property
|
||||
def hour(self):
|
||||
return self._t.tuple()[1]
|
||||
|
||||
@property
|
||||
def minute(self):
|
||||
return self._t.tuple()[2]
|
||||
|
||||
@property
|
||||
def second(self):
|
||||
return self._t.tuple()[3]
|
||||
|
||||
@property
|
||||
def microsecond(self):
|
||||
return self._t.tuple()[4]
|
||||
|
||||
@property
|
||||
def tzinfo(self):
|
||||
return self._tz
|
||||
|
||||
@property
|
||||
def fold(self):
|
||||
return self._fd
|
||||
|
||||
def __add__(self, other):
|
||||
us = self._t._us + other._us
|
||||
d, us = divmod(us, 86_400_000_000)
|
||||
d += self._d
|
||||
return datetime(0, 0, d, 0, 0, 0, us, self._tz)
|
||||
|
||||
def __sub__(self, other):
|
||||
if isinstance(other, timedelta):
|
||||
return self.__add__(-other)
|
||||
elif isinstance(other, datetime):
|
||||
d, us = self._sub(other)
|
||||
return timedelta(d, 0, us)
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
def _sub(self, other):
|
||||
# Subtract two datetime instances.
|
||||
tz1 = self._tz
|
||||
if (tz1 is None) ^ (other._tz is None):
|
||||
raise TypeError
|
||||
dt1 = self
|
||||
dt2 = other
|
||||
if tz1 is not None:
|
||||
os1 = dt1.utcoffset()
|
||||
os2 = dt2.utcoffset()
|
||||
if os1 != os2:
|
||||
dt1 -= os1
|
||||
dt2 -= os2
|
||||
D = dt1._d - dt2._d
|
||||
us = dt1._t._us - dt2._t._us
|
||||
d, us = divmod(us, 86_400_000_000)
|
||||
return D + d, us
|
||||
|
||||
def __eq__(self, other):
|
||||
if (self._tz == None) ^ (other._tz == None):
|
||||
return False
|
||||
return self._cmp(other) == 0
|
||||
|
||||
def __le__(self, other):
|
||||
return self._cmp(other) <= 0
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._cmp(other) < 0
|
||||
|
||||
def __ge__(self, other):
|
||||
return self._cmp(other) >= 0
|
||||
|
||||
def __gt__(self, other):
|
||||
return self._cmp(other) > 0
|
||||
|
||||
def _cmp(self, other):
|
||||
# Compare two datetime instances.
|
||||
d, us = self._sub(other)
|
||||
if d < 0:
|
||||
return -1
|
||||
if d > 0:
|
||||
return 1
|
||||
|
||||
if us < 0:
|
||||
return -1
|
||||
if us > 0:
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
def date(self):
|
||||
return date.fromordinal(self._d)
|
||||
|
||||
def time(self):
|
||||
return time(microsecond=self._t._us, fold=self._fd)
|
||||
|
||||
def timetz(self):
|
||||
return time(microsecond=self._t._us, tzinfo=self._tz, fold=self._fd)
|
||||
|
||||
def replace(
|
||||
self,
|
||||
year=None,
|
||||
month=None,
|
||||
day=None,
|
||||
hour=None,
|
||||
minute=None,
|
||||
second=None,
|
||||
microsecond=None,
|
||||
tzinfo=True,
|
||||
*,
|
||||
fold=None,
|
||||
):
|
||||
Y, M, D, h, m, s, us, tz, fl = self.tuple()
|
||||
if year is None:
|
||||
year = Y
|
||||
if month is None:
|
||||
month = M
|
||||
if day is None:
|
||||
day = D
|
||||
if hour is None:
|
||||
hour = h
|
||||
if minute is None:
|
||||
minute = m
|
||||
if second is None:
|
||||
second = s
|
||||
if microsecond is None:
|
||||
microsecond = us
|
||||
if tzinfo is True:
|
||||
tzinfo = tz
|
||||
if fold is None:
|
||||
fold = fl
|
||||
return datetime(year, month, day, hour, minute, second, microsecond, tzinfo, fold=fold)
|
||||
|
||||
def astimezone(self, tz=None):
|
||||
if self._tz is tz:
|
||||
return self
|
||||
_tz = self._tz
|
||||
if _tz is None:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
os = _tz.utcoffset(self)
|
||||
utc = self - os
|
||||
utc = utc.replace(tzinfo=tz)
|
||||
return tz.fromutc(utc)
|
||||
|
||||
def utcoffset(self):
|
||||
return None if self._tz is None else self._tz.utcoffset(self)
|
||||
|
||||
def dst(self):
|
||||
return None if self._tz is None else self._tz.dst(self)
|
||||
|
||||
def tzname(self):
|
||||
return None if self._tz is None else self._tz.tzname(self)
|
||||
|
||||
def timetuple(self):
|
||||
if self._tz is None:
|
||||
conv = _tmod.gmtime
|
||||
epoch = datetime.EPOCH.replace(tzinfo=None)
|
||||
else:
|
||||
conv = _tmod.localtime
|
||||
epoch = datetime.EPOCH
|
||||
return conv(round((self - epoch).total_seconds()))
|
||||
|
||||
def toordinal(self):
|
||||
return self._d
|
||||
|
||||
def timestamp(self):
|
||||
if self._tz is None:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
return (self - datetime.EPOCH).total_seconds()
|
||||
|
||||
def weekday(self):
|
||||
return (self._d + 6) % 7
|
||||
|
||||
def isoweekday(self):
|
||||
return self._d % 7 or 7
|
||||
|
||||
def isoformat(self, sep="T", timespec="auto"):
|
||||
return _d2iso(self._d) + sep + _t2iso(self._t, timespec, self, self._tz)
|
||||
|
||||
def __repr__(self):
|
||||
Y, M, D, h, m, s, us, tz, fold = self.tuple()
|
||||
tz = repr(tz)
|
||||
return "datetime.datetime({}, {}, {}, {}, {}, {}, {}, {}, fold={})".format(
|
||||
Y, M, D, h, m, s, us, tz, fold
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.isoformat(" ")
|
||||
|
||||
def __hash__(self):
|
||||
if not hasattr(self, "_hash"):
|
||||
self._hash = hash((self._d, self._t, self._tz))
|
||||
return self._hash
|
||||
|
||||
def tuple(self):
|
||||
d = _o2ymd(self._d)
|
||||
t = self._t.tuple()[1:]
|
||||
return d + t + (self._tz, self._fd)
|
||||
|
||||
|
||||
datetime.EPOCH = datetime(*_tmod.gmtime(0)[:6], tzinfo=timezone.utc)
|
|
@ -0,0 +1,84 @@
|
|||
localtz.patch
|
||||
|
||||
The CPython's implementation of `datetime.fromtimestamp()`,
|
||||
`datetime.astimezone()` and `datetime.timestamp()` for naive datetime objects
|
||||
relay on proper management of DST (daylight saving time) by `time.localtime()`
|
||||
for the timezone of interest. In the Unix port of MicroPython, this is
|
||||
accomplished by properly setting the TZ environment variable, e.g.
|
||||
`os.putenv("TZ", "Europe/Rome")`.
|
||||
|
||||
Because real boards often lack a supportive `time.localtime()`, the source code
|
||||
in `datetime.py` has been removed as to save precious resources. If your board
|
||||
provide a proper implementation, you can restore the support to naive datetime
|
||||
objects by applying this patch, e.g. `patch -p1 < localtz.patch`.
|
||||
|
||||
--- a/datetime.py
|
||||
+++ b/datetime.py
|
||||
@@ -635,7 +635,10 @@ class datetime:
|
||||
else:
|
||||
us = 0
|
||||
if tz is None:
|
||||
- raise NotImplementedError
|
||||
+ dt = cls(*_tmod.localtime(ts)[:6], microsecond=us, tzinfo=tz)
|
||||
+ s = (dt - datetime(*_tmod.localtime(ts - 86400)[:6]))._us // 1_000_000 - 86400
|
||||
+ if s < 0 and dt == datetime(*_tmod.localtime(ts + s)[:6]):
|
||||
+ dt._fd = 1
|
||||
else:
|
||||
dt = cls(*_tmod.gmtime(ts)[:6], microsecond=us, tzinfo=tz)
|
||||
dt = tz.fromutc(dt)
|
||||
@@ -812,13 +815,45 @@ class datetime:
|
||||
return self
|
||||
_tz = self._tz
|
||||
if _tz is None:
|
||||
- raise NotImplementedError
|
||||
+ ts = int(self._mktime())
|
||||
+ os = datetime(*_tmod.localtime(ts)[:6]) - datetime(*_tmod.gmtime(ts)[:6])
|
||||
else:
|
||||
os = _tz.utcoffset(self)
|
||||
utc = self - os
|
||||
utc = utc.replace(tzinfo=tz)
|
||||
return tz.fromutc(utc)
|
||||
|
||||
+ def _mktime(self):
|
||||
+ def local(u):
|
||||
+ return (datetime(*_tmod.localtime(u)[:6]) - epoch)._us // 1_000_000
|
||||
+
|
||||
+ epoch = datetime.EPOCH.replace(tzinfo=None)
|
||||
+ t, us = divmod((self - epoch)._us, 1_000_000)
|
||||
+ ts = None
|
||||
+
|
||||
+ a = local(t) - t
|
||||
+ u1 = t - a
|
||||
+ t1 = local(u1)
|
||||
+ if t1 == t:
|
||||
+ u2 = u1 + (86400 if self.fold else -86400)
|
||||
+ b = local(u2) - u2
|
||||
+ if a == b:
|
||||
+ ts = u1
|
||||
+ else:
|
||||
+ b = t1 - u1
|
||||
+ if ts is None:
|
||||
+ u2 = t - b
|
||||
+ t2 = local(u2)
|
||||
+ if t2 == t:
|
||||
+ ts = u2
|
||||
+ elif t1 == t:
|
||||
+ ts = u1
|
||||
+ elif self.fold:
|
||||
+ ts = min(u1, u2)
|
||||
+ else:
|
||||
+ ts = max(u1, u2)
|
||||
+ return ts + us / 1_000_000
|
||||
+
|
||||
def utcoffset(self):
|
||||
return None if self._tz is None else self._tz.utcoffset(self)
|
||||
|
||||
@@ -842,7 +877,7 @@ class datetime:
|
||||
|
||||
def timestamp(self):
|
||||
if self._tz is None:
|
||||
- raise NotImplementedError
|
||||
+ return self._mktime()
|
||||
else:
|
||||
return (self - datetime.EPOCH).total_seconds()
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
srctype = micropython-lib
|
||||
type = module
|
||||
version = 4.0.0
|
||||
author = Lorenzo Cappelletti
|
|
@ -0,0 +1,24 @@
|
|||
import sys
|
||||
|
||||
# Remove current dir from sys.path, otherwise setuptools will peek up our
|
||||
# module instead of system's.
|
||||
sys.path.pop(0)
|
||||
from setuptools import setup
|
||||
|
||||
sys.path.append("..")
|
||||
import sdist_upip
|
||||
|
||||
setup(
|
||||
name="micropython-datetime",
|
||||
version="4.0.0",
|
||||
description="datetime module for MicroPython",
|
||||
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
|
||||
url="https://github.com/micropython/micropython-lib",
|
||||
author="micropython-lib Developers",
|
||||
author_email="micro-python@googlegroups.com",
|
||||
maintainer="micropython-lib Developers",
|
||||
maintainer_email="micro-python@googlegroups.com",
|
||||
license="MIT",
|
||||
cmdclass={"sdist": sdist_upip.sdist},
|
||||
py_modules=["datetime"],
|
||||
)
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -1,3 +0,0 @@
|
|||
srctype = cpython
|
||||
type = module
|
||||
version = 3.3.3-1
|
|
@ -1,24 +0,0 @@
|
|||
import sys
|
||||
|
||||
# Remove current dir from sys.path, otherwise setuptools will peek up our
|
||||
# module instead of system's.
|
||||
sys.path.pop(0)
|
||||
from setuptools import setup
|
||||
|
||||
sys.path.append("..")
|
||||
import sdist_upip
|
||||
|
||||
setup(
|
||||
name="micropython-datetime",
|
||||
version="3.3.3-1",
|
||||
description="CPython datetime module ported to MicroPython",
|
||||
long_description="This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.",
|
||||
url="https://github.com/micropython/micropython-lib",
|
||||
author="CPython Developers",
|
||||
author_email="python-dev@python.org",
|
||||
maintainer="micropython-lib Developers",
|
||||
maintainer_email="micro-python@googlegroups.com",
|
||||
license="Python",
|
||||
cmdclass={"sdist": sdist_upip.sdist},
|
||||
py_modules=["datetime"],
|
||||
)
|
Plik diff jest za duży
Load Diff
Ładowanie…
Reference in New Issue