From d0d5cc4d0866d44eed652221940035d855296253 Mon Sep 17 00:00:00 2001 From: jesopo Date: Thu, 30 Jan 2020 21:12:31 +0000 Subject: split utils.datetime out in to .parse and .format --- src/LockFile.py | 4 +- src/Logging.py | 2 +- src/core_modules/format_activity.py | 4 +- src/core_modules/ircv3_server_time.py | 2 +- src/core_modules/silence.py | 2 +- src/utils/datetime.py | 135 ---------------------------------- src/utils/datetime/__init__.py | 2 + src/utils/datetime/common.py | 42 +++++++++++ src/utils/datetime/format.py | 77 +++++++++++++++++++ src/utils/datetime/parse.py | 25 +++++++ src/utils/parse/time.py | 2 +- 11 files changed, 154 insertions(+), 143 deletions(-) delete mode 100644 src/utils/datetime.py create mode 100644 src/utils/datetime/__init__.py create mode 100644 src/utils/datetime/common.py create mode 100644 src/utils/datetime/format.py create mode 100644 src/utils/datetime/parse.py (limited to 'src') diff --git a/src/LockFile.py b/src/LockFile.py index 54ff6284..2765e87d 100644 --- a/src/LockFile.py +++ b/src/LockFile.py @@ -14,7 +14,7 @@ class LockFile(PollHook.PollHook): with open(self._filename, "r") as lock_file: timestamp_str = lock_file.read().strip().split(" ", 1)[0] - timestamp = utils.datetime.iso8601_parse(timestamp_str) + timestamp = utils.datetime.parse.iso8601(timestamp_str) if (now-timestamp).total_seconds() < EXPIRATION: return False @@ -24,7 +24,7 @@ class LockFile(PollHook.PollHook): def lock(self): with open(self._filename, "w") as lock_file: last_lock = utils.datetime.utcnow() - lock_file.write("%s" % utils.datetime.iso8601_format(last_lock)) + lock_file.write("%s" % utils.datetime.format.iso8601(last_lock)) self._next_lock = last_lock+datetime.timedelta( seconds=EXPIRATION/2) diff --git a/src/Logging.py b/src/Logging.py index 0f636587..ec8574bd 100644 --- a/src/Logging.py +++ b/src/Logging.py @@ -13,7 +13,7 @@ LEVELS = { class BitBotFormatter(logging.Formatter): def formatTime(self, record, datefmt=None): datetime_obj = datetime.datetime.fromtimestamp(record.created) - return utils.datetime.iso8601_format(datetime_obj, + return utils.datetime.format.iso8601(datetime_obj, timespec=utils.datetime.TimeSpec.MILLISECOND) class HookedHandler(logging.StreamHandler): diff --git a/src/core_modules/format_activity.py b/src/core_modules/format_activity.py index 8bffd64d..cad74db3 100644 --- a/src/core_modules/format_activity.py +++ b/src/core_modules/format_activity.py @@ -197,8 +197,8 @@ class Module(ModuleManager.BaseModule): self._on_topic(event, event["setter"].nickname, "set", event["channel"].topic) - dt = utils.datetime.datetime_human( - utils.datetime.datetime_timestamp(event["set_at"])) + dt = utils.datetime.format.datetime_human( + utils.datetime.timestamp(event["set_at"])) minimal = "topic set at %s" % dt line = "- %s" % minimal diff --git a/src/core_modules/ircv3_server_time.py b/src/core_modules/ircv3_server_time.py index 270d56d9..ebc19aad 100644 --- a/src/core_modules/ircv3_server_time.py +++ b/src/core_modules/ircv3_server_time.py @@ -21,5 +21,5 @@ class Module(ModuleManager.BaseModule): def message(self, event): server_time = self._get(event["line"].tags) if not server_time == None: - dt = utils.datetime.iso8601_parse(server_time) + dt = utils.datetime.parse.iso8601(server_time) event["buffer_line"].timestamp = dt diff --git a/src/core_modules/silence.py b/src/core_modules/silence.py index 42990921..70dd3a4d 100644 --- a/src/core_modules/silence.py +++ b/src/core_modules/silence.py @@ -30,7 +30,7 @@ class Module(ModuleManager.BaseModule): def silence(self, event): duration = SILENCE_TIME if event["args"] and event["args_split"][0].startswith("+"): - duration = utils.datetime.from_pretty_time( + duration = utils.datetime.parse.from_pretty_time( event["args_split"][0][1:]) if duration == None: raise utils.EventError("Invalid duration provided") diff --git a/src/utils/datetime.py b/src/utils/datetime.py deleted file mode 100644 index 650893d1..00000000 --- a/src/utils/datetime.py +++ /dev/null @@ -1,135 +0,0 @@ -import enum, re, typing -import datetime as _datetime -import dateutil.parser, dateutil.relativedelta - -ISO8601_FORMAT_DT = "%Y-%m-%dT%H:%M:%S" -ISO8601_FORMAT_TZ = "%z" - -TIME_HUMAN = "%H:%M:%S" -DATE_HUMAN = "%Y-%m-%d" - -class TimeSpec(enum.Enum): - NORMAL = 1 - MILLISECOND = 2 - -def utcnow() -> _datetime.datetime: - return _datetime.datetime.utcnow().replace(tzinfo=_datetime.timezone.utc) -def datetime_timestamp(seconds: float) -> _datetime.datetime: - return _datetime.datetime.fromtimestamp(seconds).replace( - tzinfo=_datetime.timezone.utc) - -def iso8601_format(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL - ) -> str: - dt_format = dt.strftime(ISO8601_FORMAT_DT) - tz_format = dt.strftime(ISO8601_FORMAT_TZ) - - ms_format = "" - if timespec == TimeSpec.MILLISECOND: - ms_format = ".%s" % str(int(dt.microsecond/1000)).zfill(3) - - return "%s%s%s" % (dt_format, ms_format, tz_format) -def iso8601_format_now(timespec: TimeSpec=TimeSpec.NORMAL) -> str: - return iso8601_format(utcnow(), timespec) - -def iso8601_parse(s: str) -> _datetime.datetime: - return dateutil.parser.parse(s) - -def datetime_human(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL): - date = _datetime.datetime.strftime(dt, DATE_HUMAN) - time = _datetime.datetime.strftime(dt, TIME_HUMAN) - if timespec == TimeSpec.MILLISECOND: - time += ".%s" % str(int(dt.microsecond/1000)).zfill(3) - return "%s %s" % (date, time) -def date_human(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL): - return _datetime.datetime.strftime(dt, DATE_HUMAN) - -def seconds_since(dt: _datetime.datetime) -> float: - return (utcnow()-dt).total_seconds() - -TIME_SECOND = 1 -TIME_MINUTE = TIME_SECOND*60 -TIME_HOUR = TIME_MINUTE*60 -TIME_DAY = TIME_HOUR*24 -TIME_WEEK = TIME_DAY*7 - -def time_unit(seconds: int) -> typing.Tuple[int, str]: - since = None - unit = None - if seconds >= TIME_WEEK: - since = seconds/TIME_WEEK - unit = "week" - elif seconds >= TIME_DAY: - since = seconds/TIME_DAY - unit = "day" - elif seconds >= TIME_HOUR: - since = seconds/TIME_HOUR - unit = "hour" - elif seconds >= TIME_MINUTE: - since = seconds/TIME_MINUTE - unit = "minute" - else: - since = seconds - unit = "second" - since = int(since) - if since > 1: - unit = "%ss" % unit # pluralise the unit - return (since, unit) - -REGEX_PRETTYTIME = re.compile( - r"(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?", re.I) - -SECONDS_MINUTES = 60 -SECONDS_HOURS = SECONDS_MINUTES*60 -SECONDS_DAYS = SECONDS_HOURS*24 -SECONDS_WEEKS = SECONDS_DAYS*7 - -def from_pretty_time(pretty_time: str) -> typing.Optional[int]: - seconds = 0 - - match = re.match(REGEX_PRETTYTIME, pretty_time) - if match: - seconds += int(match.group(1) or 0)*SECONDS_WEEKS - seconds += int(match.group(2) or 0)*SECONDS_DAYS - seconds += int(match.group(3) or 0)*SECONDS_HOURS - seconds += int(match.group(4) or 0)*SECONDS_MINUTES - seconds += int(match.group(5) or 0) - - if seconds > 0: - return seconds - return None - -UNIT_MINIMUM = 6 -UNIT_SECOND = 5 -UNIT_MINUTE = 4 -UNIT_HOUR = 3 -UNIT_DAY = 2 -UNIT_WEEK = 1 -UNIT_MONTH = 1 -UNIT_YEAR = 1 -def to_pretty_time(total_seconds: int, minimum_unit: int=UNIT_SECOND, - max_units: int=UNIT_MINIMUM) -> str: - if total_seconds == 0: - return "0s" - - now = utcnow() - later = now+_datetime.timedelta(seconds=total_seconds) - relative = dateutil.relativedelta.relativedelta(later, now) - - out: typing.List[str] = [] - if relative.years and minimum_unit >= UNIT_YEAR and len(out) < max_units: - out.append("%dy" % relative.years) - if relative.months and minimum_unit >= UNIT_MONTH and len(out) < max_units: - out.append("%dmo" % relative.months) - if relative.weeks and minimum_unit >= UNIT_WEEK and len(out) < max_units: - out.append("%dw" % relative.weeks) - if relative.days and minimum_unit >= UNIT_DAY and len(out) < max_units: - out.append("%dd" % relative.days) - if relative.hours and minimum_unit >= UNIT_HOUR and len(out) < max_units: - out.append("%dh" % relative.hours) - if relative.minutes and minimum_unit >= UNIT_MINUTE and len(out) < max_units: - out.append("%dmi" % relative.minutes) - if relative.seconds and minimum_unit >= UNIT_SECOND and len(out) < max_units: - out.append("%ds" % relative.seconds) - - return " ".join(out) - diff --git a/src/utils/datetime/__init__.py b/src/utils/datetime/__init__.py new file mode 100644 index 00000000..dbe546db --- /dev/null +++ b/src/utils/datetime/__init__.py @@ -0,0 +1,2 @@ +from .common import * +from . import format, parse diff --git a/src/utils/datetime/common.py b/src/utils/datetime/common.py new file mode 100644 index 00000000..7235fbab --- /dev/null +++ b/src/utils/datetime/common.py @@ -0,0 +1,42 @@ +import datetime as _datetime +import enum + +ISO8601_FORMAT_DT = "%Y-%m-%dT%H:%M:%S" +ISO8601_FORMAT_TZ = "%z" + +TIME_HUMAN = "%H:%M:%S" +DATE_HUMAN = "%Y-%m-%d" + +class TimeSpec(enum.Enum): + NORMAL = 1 + MILLISECOND = 2 + +TIME_SECOND = 1 +TIME_MINUTE = TIME_SECOND*60 +TIME_HOUR = TIME_MINUTE*60 +TIME_DAY = TIME_HOUR*24 +TIME_WEEK = TIME_DAY*7 + +SECONDS_MINUTES = 60 +SECONDS_HOURS = SECONDS_MINUTES*60 +SECONDS_DAYS = SECONDS_HOURS*24 +SECONDS_WEEKS = SECONDS_DAYS*7 + +UNIT_MINIMUM = 6 +UNIT_SECOND = 5 +UNIT_MINUTE = 4 +UNIT_HOUR = 3 +UNIT_DAY = 2 +UNIT_WEEK = 1 +UNIT_MONTH = 1 +UNIT_YEAR = 1 + +def utcnow() -> _datetime.datetime: + return _datetime.datetime.utcnow().replace(tzinfo=_datetime.timezone.utc) + +def timestamp(seconds: float) -> _datetime.datetime: + return _datetime.datetime.fromtimestamp(seconds).replace( + tzinfo=_datetime.timezone.utc) + +def seconds_since(dt: _datetime.datetime) -> float: + return (utcnow()-dt).total_seconds() diff --git a/src/utils/datetime/format.py b/src/utils/datetime/format.py new file mode 100644 index 00000000..feefde82 --- /dev/null +++ b/src/utils/datetime/format.py @@ -0,0 +1,77 @@ +import typing +import datetime as _datetime +import dateutil.relativedelta +from .common import * + +def iso8601(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL + ) -> str: + dt_format = dt.strftime(ISO8601_FORMAT_DT) + tz_format = dt.strftime(ISO8601_FORMAT_TZ) + + ms_format = "" + if timespec == TimeSpec.MILLISECOND: + ms_format = ".%s" % str(int(dt.microsecond/1000)).zfill(3) + + return "%s%s%s" % (dt_format, ms_format, tz_format) +def iso8601_format_now(timespec: TimeSpec=TimeSpec.NORMAL) -> str: + return iso8601(utcnow(), timespec) + +def datetime_human(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL): + date = _datetime.datetime.strftime(dt, DATE_HUMAN) + time = _datetime.datetime.strftime(dt, TIME_HUMAN) + if timespec == TimeSpec.MILLISECOND: + time += ".%s" % str(int(dt.microsecond/1000)).zfill(3) + return "%s %s" % (date, time) +def date_human(dt: _datetime.datetime, timespec: TimeSpec=TimeSpec.NORMAL): + return _datetime.datetime.strftime(dt, DATE_HUMAN) + +def time_unit(seconds: int) -> typing.Tuple[int, str]: + since = None + unit = None + if seconds >= TIME_WEEK: + since = seconds/TIME_WEEK + unit = "week" + elif seconds >= TIME_DAY: + since = seconds/TIME_DAY + unit = "day" + elif seconds >= TIME_HOUR: + since = seconds/TIME_HOUR + unit = "hour" + elif seconds >= TIME_MINUTE: + since = seconds/TIME_MINUTE + unit = "minute" + else: + since = seconds + unit = "second" + since = int(since) + if since > 1: + unit = "%ss" % unit # pluralise the unit + return (since, unit) + +def to_pretty_time(total_seconds: int, minimum_unit: int=UNIT_SECOND, + max_units: int=UNIT_MINIMUM) -> str: + if total_seconds == 0: + return "0s" + + now = utcnow() + later = now+_datetime.timedelta(seconds=total_seconds) + relative = dateutil.relativedelta.relativedelta(later, now) + + out: typing.List[str] = [] + if relative.years and minimum_unit >= UNIT_YEAR and len(out) < max_units: + out.append("%dy" % relative.years) + if relative.months and minimum_unit >= UNIT_MONTH and len(out) < max_units: + out.append("%dmo" % relative.months) + if relative.weeks and minimum_unit >= UNIT_WEEK and len(out) < max_units: + out.append("%dw" % relative.weeks) + if relative.days and minimum_unit >= UNIT_DAY and len(out) < max_units: + out.append("%dd" % relative.days) + if relative.hours and minimum_unit >= UNIT_HOUR and len(out) < max_units: + out.append("%dh" % relative.hours) + if relative.minutes and minimum_unit >= UNIT_MINUTE and len(out) < max_units: + out.append("%dmi" % relative.minutes) + if relative.seconds and minimum_unit >= UNIT_SECOND and len(out) < max_units: + out.append("%ds" % relative.seconds) + + return " ".join(out) + diff --git a/src/utils/datetime/parse.py b/src/utils/datetime/parse.py new file mode 100644 index 00000000..8e372f8c --- /dev/null +++ b/src/utils/datetime/parse.py @@ -0,0 +1,25 @@ +import re, typing +import datetime as _datetime +import dateutil.parser +from .common import * + +def iso8601(s: str) -> _datetime.datetime: + return dateutil.parser.parse(s) + +REGEX_PRETTYTIME = re.compile( + r"(?:(\d+)w)?(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?", re.I) + +def from_pretty_time(pretty_time: str) -> typing.Optional[int]: + seconds = 0 + + match = re.match(REGEX_PRETTYTIME, pretty_time) + if match: + seconds += int(match.group(1) or 0)*SECONDS_WEEKS + seconds += int(match.group(2) or 0)*SECONDS_DAYS + seconds += int(match.group(3) or 0)*SECONDS_HOURS + seconds += int(match.group(4) or 0)*SECONDS_MINUTES + seconds += int(match.group(5) or 0) + + if seconds > 0: + return seconds + return None diff --git a/src/utils/parse/time.py b/src/utils/parse/time.py index 51094c2d..217b920a 100644 --- a/src/utils/parse/time.py +++ b/src/utils/parse/time.py @@ -2,7 +2,7 @@ from src.utils import datetime def duration(s: str): if s[0] == "+": - duration = datetime.from_pretty_time(s[1:]) + duration = datetime.parse.from_pretty_time(s[1:]) if not duration == None: return duration return None -- cgit v1.3.1-10-gc9f91