diff --git a/src/tww/tokenizer.py b/src/tww/tokenizer.py index ed09e25..c3d0c6a 100644 --- a/src/tww/tokenizer.py +++ b/src/tww/tokenizer.py @@ -1,12 +1,13 @@ import json import re +import sys from datetime import datetime from pygments import highlight, lexers, formatters -from tww import ISO_FORMAT, time_to_emoji +from tww import ISO_FORMAT, time_to_emoji, time_ago from tww import resolve_timezone, dateparser_parse_dt, get_utcnow, get_s_since_epoch, get_ms_since_epoch, \ - dt_tz_translation, DEFAULT_FORMAT, get_local_now, query_to_format_result + dt_tz_translation, get_local_now, query_to_format_result r_generic = re.compile('(.*)', flags=re.IGNORECASE) r_time_in_epoch_s_now = re.compile('(?:time since epoch|seconds since epoch)', flags=re.IGNORECASE) @@ -48,34 +49,30 @@ def handler_time_ms_now_utc() -> int: return get_ms_since_epoch(get_utcnow()) -def handler_time_diff(start_dt: str, end_dt: str) -> dict: - start, end = dateparser_parse_dt(start_dt), dateparser_parse_dt(end_dt) - return dict(start=start.strftime(DEFAULT_FORMAT), - end=end.strftime(DEFAULT_FORMAT), - diff=end - start) +def handler_time_diff(start_dt: datetime, end_dt: datetime) -> dict: + diff = end_dt - start_dt + return dict(start=dt_pretty(start_dt), + end=dt_pretty(end_dt), + diff=td_pretty(diff)) -def handler_time_since(start_dt_s: str) -> dict: - return handler_time_diff(start_dt_s, str(get_utcnow())) - - -def handler_time_until(end_dt_s: str) -> dict: - return handler_time_diff(str(get_utcnow()), end_dt_s) +def handler_time_since_until(start_dt_s: str) -> dict: + return handler_time_diff(dateparser_parse_dt(start_dt_s), get_local_now()) def handler_timezone_translation(dt_s: str, timezone_like_s: str) -> dict: - dt = dateparser_parse_dt(dt_s) + src_dt = dateparser_parse_dt(dt_s) tz = resolve_timezone(timezone_like_s) if not tz: - tz, offset, the_time = '', '', dt + tz, dst_dt = {}, src_dt else: offset = tz.get('tz_offset') - translation = dt_tz_translation(dt, offset) - the_time = translation.strftime(DEFAULT_FORMAT) - return dict(dt=the_time, - offset=offset, - tz=tz, - ) + dst_dt = dt_tz_translation(src_dt, offset) + return dict( + src_dt=dt_pretty(src_dt), + dst_dt=dt_pretty(dst_dt), + tz=tz, + ) def handler_generic_parser(dt_s: str) -> datetime: @@ -109,8 +106,8 @@ regex_handlers = [ (r_time_in_epoch_ms2, handler_time_ms, QUERY_TYPE_DT), (r_time_in_epoch_ms3, handler_time_ms, QUERY_TYPE_DT), (r_timezone_translation, handler_timezone_translation, QUERY_TYPE_DT_TR), - (r_time_since, handler_time_since, QUERY_TYPE_TD), - (r_time_until, handler_time_until, QUERY_TYPE_TD), + (r_time_since, handler_time_since_until, QUERY_TYPE_TD), + (r_time_until, handler_time_since_until, QUERY_TYPE_TD), (r_time_between, handler_time_diff, QUERY_TYPE_TD), (r_time_in, handler_time_in_parser, QUERY_TYPE_DT), (r_timezone, handler_timezone, QUERY_TYPE_TZ), @@ -140,7 +137,7 @@ def tokenize(s): except Exception as e: continue if result is not None: - solutions.append((h.__name__, (result, ), t)) + solutions.append((h.__name__, (result,), t)) return solutions @@ -197,6 +194,74 @@ def dt_pretty(dt): return rv +def td_remainders(td): + # split seconds to larger units + seconds = td.total_seconds() + minutes, seconds = divmod(seconds, 60) + hours, minutes = divmod(minutes, 60) + days, hours = divmod(hours, 24) + months, days = divmod(days, 30.42) + years, months = divmod(months, 12) + years, months, days, hours, minutes, seconds = map(int, (years, months, days, hours, minutes, seconds)) + years, months, days, hours, minutes, seconds = map(abs, (years, months, days, hours, minutes, seconds)) + return dict( + seconds=seconds, + minutes=minutes, + hours=hours, + days=days, + months=months, + years=years, + ) + + +def td_totals(td): + seconds = td.total_seconds() + minutes = seconds // 60 + hours = seconds // (60 * 60) + days = seconds // (24 * 60 * 60) + weeks = seconds // (7 * 24 * 60 * 60) + months = seconds // (30 * 24 * 60 * 60) + years = seconds // (365 * 24 * 60 * 60) + years, months, weeks, days, hours, minutes, seconds = map(abs, (years, months, weeks, days, hours, minutes, seconds)) + return dict( + seconds=seconds, + minutes=minutes, + hours=hours, + days=days, + weeks=weeks, + months=months, + years=years, + ) + + +def td_iso8601(td): + """P[n]Y[n]M[n]DT[n]H[n]M[n]S""" + rem = td_remainders(td) + fmt = "P" + for short, timeframe in [("Y", "years"), ("M", "months"), ("D", "days")]: + if rem[timeframe]: + fmt += "{}{}".format(rem[timeframe], short) + hms = [("H", "hours"), ("M", "minutes"), ("S", "seconds")] + if any([rem[t[1]] for t in hms]): + fmt += "T" + for short, timeframe in hms: + if rem[timeframe]: + fmt += "{}{}".format(rem[timeframe], short) + return fmt + + +def td_pretty(td): + rv = { + "sign": '-' if td.days < 0 else '+', + "in_the": 'future' if td.days < 0 else 'past', + "time_ago": time_ago(td), + "duration_iso8601": td_iso8601(td), + "totals": td_totals(td), + "remainders": td_remainders(td), + } + return rv + + def resolve_query_type(query): solutions = tokenize(query) if not solutions: @@ -230,7 +295,7 @@ def resolve_query(query): if __name__ == "__main__": - query = "now in sofia" - # query = ' '.join(sys.argv[1:]) + # query = "time since 27 January 1992" + query = ' '.join(sys.argv[1:]) result = resolve_query(query) pretty_print_dict(result)