From cd3a9e894388d3ff81f9567f8d3063ef5ff71142 Mon Sep 17 00:00:00 2001 From: Daniel Tsvetkov Date: Wed, 18 Aug 2021 15:53:40 +0300 Subject: [PATCH] time conversion --- README.md | 3 +++ tww/lib.py | 2 ++ tww/tokenizer.py | 28 +++++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3fa1faf..6a61550 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,9 @@ python tww QUERY [--debug] [--full] [--show=] - `05:23 - 150 minutes` - `3 days from next Friday` - `2 hours before 15:00` + - Time conversion: `* (years|months|weeks|days|hours|minutes|seconds) to (years|months|weeks|days|hours|minutes|seconds)` + - `108 hours to minutes` + - `314 minutes` (implicit conversion to automatic human-friendly output) - (Approximate) workdays calculation (assumes monday-friday are work days - ignores public/local holidays (for now)): `work days/hours since/until ` or ` - `workdays since 2021-01-05` - `work hours until Friday` diff --git a/tww/lib.py b/tww/lib.py index e8ce18b..9215cc5 100644 --- a/tww/lib.py +++ b/tww/lib.py @@ -845,6 +845,8 @@ def td_totals(td): years, months, weeks, days, hours, minutes, seconds = map(abs, (years, months, weeks, days, hours, minutes, seconds)) return dict( + milliseconds=seconds*1e3, + microseconds=seconds*1e6, seconds=seconds, minutes=minutes, hours=hours, diff --git a/tww/tokenizer.py b/tww/tokenizer.py index e3ee433..e1c00c9 100644 --- a/tww/tokenizer.py +++ b/tww/tokenizer.py @@ -30,6 +30,8 @@ r_time_in = re.compile('(?:time)?\s*in\s*(.*)', flags=re.IGNORECASE) r_time_since = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*(?:since|ago)\s*(.*)', flags=re.IGNORECASE) r_time_until = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*(?:until|to)\s*(.*)', flags=re.IGNORECASE) r_time_between = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*between\s*(.*)\s*and\s*(.*)', flags=re.IGNORECASE) +r_time_conversion = re.compile('\s*(.*)\s*(year|month|week|day|hour|minute|second|microsecond|millisecond)(?:s)?', flags=re.IGNORECASE) +r_time_conversion_to = re.compile('\s*(.*)\s*(year|month|week|day|hour|minute|second|microsecond|millisecond)(?:s)?\s*to\s*(year|month|week|day|hour|minute|second|microsecond|millisecond)(?:s)', flags=re.IGNORECASE) r_tz_between = re.compile('(?:time difference|tz diff|time diff|time zone difference|timezone difference|timezone diff|time zone diff)?(?:s)?\s*between\s*(.*)\s*and\s*(.*)', flags=re.IGNORECASE) r_time_plus = re.compile('(.*)\s*(?:plus|\+|after|from)\s*(.*)', flags=re.IGNORECASE) r_time_minus = re.compile('(.*)\s*(?:minus|\-)\s*(.*)', flags=re.IGNORECASE) @@ -88,6 +90,24 @@ def handler_time_diff(start_dt, end_dt) -> dict: diff=td_pretty(diff)) +def handler_time_conversion(time_d, input_f, output_f=None) -> dict: + time_d = int(time_d) + if input_f in ['year']: + td = timedelta(days=365*time_d) + elif input_f in ['month']: + td = timedelta(days=30*time_d) + elif input_f in ['week', 'day', 'hour', 'minute', 'second', 'microsecond', 'millisecond']: + td = timedelta(**{"{}s".format(input_f): time_d}) + else: + td = timedelta() + td_p = td_pretty(td) + if not output_f: + out = td_p['duration_human'] + else: + out = "{} {}s".format(td_p['totals']["{}s".format(output_f)], output_f) + return dict(out=out, timedelta=td_p) + + def handler_time_since_until(start_dt_s: str) -> dict: return handler_time_diff(start_dt_s, get_local_now()) @@ -210,6 +230,7 @@ QUERY_TYPE_DT_TR = "datetime_translation" QUERY_TYPE_DT = "datetime_details" QUERY_TYPE_TZ = "timezone" QUERY_TYPE_TD = "timedelta" +QUERY_TYPE_CONV = "conversion" QUERY_TYPE_CAL = "calendar" h_default = '' @@ -223,11 +244,14 @@ h_time_in = 'dt->hh:mm' h_translation = 'dt->iso8601_full' h_default_dt = 'dt->iso8601_full' h_default_td = 'timedelta->diff->duration_human' +h_default_conv = 'conv->out' h_day_of_week = 'dt->locale_day_of_week' h_cal_year = 'cal->year' h_cal_month = 'cal->month' regex_handlers = [ + (r_time_conversion_to, handler_time_conversion, QUERY_TYPE_CONV, [h_default_conv]), + (r_time_conversion, handler_time_conversion, QUERY_TYPE_CONV, [h_default_conv]), (r_time_in_epoch_s_now, handler_time_now_local, QUERY_TYPE_DT, [h_unix_s]), (r_time_in_epoch_s_now, handler_time_now_utc, QUERY_TYPE_DT, [h_unix_s]), (r_time_in_epoch_s2, handler_generic_parser, QUERY_TYPE_DT, [h_unix_s]), @@ -391,7 +415,7 @@ def resolve_query(query, allowed_queries=None): } solutions = resolve_query_type(query) if not allowed_queries: - allowed_queries = [QUERY_TYPE_DT, QUERY_TYPE_DT_TR, QUERY_TYPE_TD, QUERY_TYPE_TZ, QUERY_TYPE_CAL] + allowed_queries = [QUERY_TYPE_DT, QUERY_TYPE_DT_TR, QUERY_TYPE_TD, QUERY_TYPE_TZ, QUERY_TYPE_CAL, QUERY_TYPE_CONV] for sol_id, solution in enumerate(solutions): element = {} handler, results, query_type, hi = solution @@ -414,6 +438,8 @@ def resolve_query(query, allowed_queries=None): element["timedelta"] = results elif query_type == QUERY_TYPE_CAL: element["cal"] = results + elif query_type == QUERY_TYPE_CONV: + element["conv"] = results rv["solutions"].append(element) except Exception as e: continue