Files
admins 702d080a1e New Addon
Google BK
2023-04-23 18:17:03 +07:00

176 lines
5.2 KiB
Python

import asyncio
from datetime import datetime, timedelta
from injector import inject, singleton
from dateutil.relativedelta import relativedelta
from dateutil.parser import parse
from .logger import getLogger
import pytz
import os
from pytz import timezone, utc
from tzlocal import get_localzone_name, get_localzone
from dateutil.tz import tzlocal
logger = getLogger(__name__)
def get_local_tz():
methods = [
_infer_timezone_from_env,
_infer_timezone_from_system,
_infer_timezone_from_name,
_infer_timezone_from_offset
]
for method in methods:
try:
tz = method()
if tz is not None:
return tz
except Exception:
pass
return utc
def _infer_timezone_from_offset():
now = datetime.now()
desired_offset = tzlocal().utcoffset(now)
for tz_name in pytz.all_timezones:
tz = pytz.timezone(tz_name)
if desired_offset == tz.utcoffset(now):
return tz
return None
def _infer_timezone_from_name():
name = get_localzone_name()
if name is not None:
return timezone(name)
return None
def _infer_timezone_from_system():
tz = get_localzone()
if tz is not None:
return tz
return None
def _infer_timezone_from_env():
if "TZ" in os.environ:
tz = timezone(os.environ["TZ"])
if tz is not None:
return tz
return None
@singleton
class Time(object):
@inject
def __init__(self, local_tz=get_local_tz()):
self.local_tz = local_tz
self._offset = timedelta(seconds=0)
def now(self) -> datetime:
return datetime.now(pytz.utc) + self._offset
def nowLocal(self) -> datetime:
return datetime.now(self.local_tz) + self._offset
@property
def offset(self):
return self._offset
@offset.setter
def offset(self, delta: timedelta):
self._offset = delta
@classmethod
def parse(cls, text: str) -> datetime:
ret = parse(text)
if ret.tzinfo is None:
ret = ret.replace(tzinfo=utc)
return ret
def toLocal(self, dt: datetime) -> datetime:
return dt.astimezone(self.local_tz)
def localize(self, dt: datetime) -> datetime:
return self.local_tz.localize(dt)
def toUtc(self, dt: datetime) -> datetime:
return dt.astimezone(utc)
async def sleepAsync(self, seconds: float, early_exit: asyncio.Event = None) -> None:
if early_exit is None:
await asyncio.sleep(seconds)
else:
try:
await asyncio.wait_for(early_exit.wait(), seconds)
except asyncio.TimeoutError:
pass
def local(self, year, month, day, hour=0, minute=0, second=0, ms=0):
return self.local_tz.localize(datetime(year, month, day, hour, minute, second, ms))
def formatDelta(self, time: datetime, now=None) -> str:
if not now:
now = self.now()
delta: relativedelta = None
flavor = ""
if time < now:
delta = relativedelta(now, time)
flavor = " ago"
else:
delta = relativedelta(time, now)
flavor = ""
if delta.years > 0:
plural = "s" if delta.years > 1 else ""
return "{0} year{2}{1}".format(delta.years, flavor, plural)
if (delta.months != 0):
plural = "s" if delta.months > 1 else ""
if delta.days > 15:
return "{0} month{2}{1}".format(delta.months + 1, flavor, plural)
return "{0} month{2}{1}".format(delta.months, flavor, plural)
if (delta.days != 0):
plural = "s" if delta.days > 1 else ""
if delta.hours >= 12:
return "{0} day{2}{1}".format(delta.days + 1, flavor, plural)
return "{0} day{2}{1}".format(delta.days, flavor, plural)
if (delta.hours != 0):
plural = "s" if delta.hours > 1 else ""
if delta.minutes >= 30:
return "{0} hour{2}{1}".format(delta.hours + 1, flavor, plural)
return "{0} hour{2}{1}".format(delta.hours, flavor, plural)
if (delta.minutes != 0):
plural = "s" if delta.minutes > 1 else ""
if delta.minutes >= 30:
return "{0} minute{2}{1}".format(delta.minutes + 1, flavor, plural)
return "{0} minute{2}{1}".format(delta.minutes, flavor, plural)
if (delta.seconds != 0):
plural = "s" if delta.seconds > 1 else ""
return "{0} second{2}{1}".format(delta.seconds, flavor, plural)
return "right now"
def asRfc3339String(self, time: datetime) -> str:
if time is None:
time = self.now()
return time.strftime("%Y-%m-%dT%H:%M:%SZ")
class AcceleratedTime(Time):
def __init__(self, dialation=1.0):
super().__init__()
self.start = datetime.now(utc)
self.dialation = dialation
def now(self):
return self.start + timedelta(seconds=(datetime.now(utc) - self.start).total_seconds() * self.dialation)
def nowLocal(self) -> datetime:
return self.localize(self.now())
async def sleepAsync(self, seconds: float) -> None:
await asyncio.sleep(seconds / self.dialation)