calendar printing

This commit is contained in:
Daniel Tsvetkov 2021-05-13 14:39:49 +02:00
parent b897831827
commit 9523ad480f
3 changed files with 60 additions and 12 deletions

View File

@ -28,6 +28,8 @@ python tww QUERY [--debug] [--full] [--show=<param>]
- `time between 2012-03-14 and 2012-04-26` - `time between 2012-03-14 and 2012-04-26`
- `time since 09:00` - `time since 09:00`
- `time until end of workday` - `time until end of workday`
- Timezone difference: `timezone difference between <tz-like> and <tz-like>` or `tz diff between <tz-like> and <tz-like>`
- `timezone difference between sofia and portugal`
- Calculate time differences: `<dt-like> (?\+|\-|plus|minus) <timedelta>` or `<timedelta> (before|from) <dt>` - Calculate time differences: `<dt-like> (?\+|\-|plus|minus) <timedelta>` or `<timedelta> (before|from) <dt>`
- `12-12-2019 + 2 weeks` - `12-12-2019 + 2 weeks`
- `05:23 - 150 minutes` - `05:23 - 150 minutes`
@ -44,6 +46,9 @@ python tww QUERY [--debug] [--full] [--show=<param>]
- `2021-05-10 day of week` - `2021-05-10 day of week`
- Find timezones - Find timezones
- `timezone in Brazil` - `timezone in Brazil`
- Print calendar `cal(endar) (month) <dt-like>` or `cal year <dt-like>` (for whole year)
- `cal year 2021`
- `calendar january 2018`

View File

@ -290,6 +290,7 @@ def find_from_offset(query):
def resolve_timezone(query, dt=None): def resolve_timezone(query, dt=None):
if not query: if not query:
query = "utc" query = "utc"
dt = dt or datetime.now()
# if the human_tz_loc contains /, assume it's a timezone which could be # if the human_tz_loc contains /, assume it's a timezone which could be
# incorrectly written with small letters - need Continent/City # incorrectly written with small letters - need Continent/City
normal_query = query.lower().strip() normal_query = query.lower().strip()
@ -301,6 +302,7 @@ def resolve_timezone(query, dt=None):
"normal_query": normal_query, "normal_query": normal_query,
"tz_offset": local_offset, "tz_offset": local_offset,
"tz_name": local_iana, "tz_name": local_iana,
'is_local': True,
} }
found_from_iana_tz = NORMALIZED_TZ_DICT.get(normal_query, "") found_from_iana_tz = NORMALIZED_TZ_DICT.get(normal_query, "")
found_from_abbr_tzs = list(NORMALIZED_TZ_ABBR.get(normal_query, set())) found_from_abbr_tzs = list(NORMALIZED_TZ_ABBR.get(normal_query, set()))
@ -343,10 +345,11 @@ def resolve_timezone(query, dt=None):
except UnknownTimeZoneError: except UnknownTimeZoneError:
pytz_result = type('pytz', (), {"zone": ""}) pytz_result = type('pytz', (), {"zone": ""})
tz_name = pytz_result.zone tz_name = pytz_result.zone
tz_dst_seconds = pytz_result.dst(dt.replace(tzinfo=None) or datetime.now()).seconds tz_dst_seconds = pytz_result.dst(dt.replace(tzinfo=None)).seconds
pytz_localized = pytz_result.localize(dt.replace(tzinfo=None) or datetime.now()) pytz_localized = pytz_result.localize(dt.replace(tzinfo=None))
tz_abbr = pytz_localized.strftime('%Z') if tz_name else "" tz_abbr = pytz_localized.strftime('%Z') if tz_name else ""
tz_offset = pytz_localized.strftime('%z') if tz_name else "" tz_offset = pytz_localized.strftime('%z') if tz_name else ""
tz_offset_seconds = offset_to_seconds(tz_offset)
return { return {
"query": query, "query": query,
"normal_query": normal_query, "normal_query": normal_query,
@ -361,8 +364,10 @@ def resolve_timezone(query, dt=None):
"tz_name": tz_name, "tz_name": tz_name,
"tz_abbr": tz_abbr, "tz_abbr": tz_abbr,
"tz_offset": tz_offset, "tz_offset": tz_offset,
"tz_offset_seconds": tz_offset_seconds,
"tz_dst_seconds": tz_dst_seconds, "tz_dst_seconds": tz_dst_seconds,
"tz_is_dst": tz_dst_seconds != 0, "tz_is_dst": tz_dst_seconds != 0,
"is_local": False,
} }
@ -623,6 +628,11 @@ def split_offset(offset):
return int(to_shh), int(to_mm) return int(to_shh), int(to_mm)
def offset_to_seconds(offset):
h, m = split_offset(offset)
return h * 3600 + m * 60
def dt_tz_translation(dt: datetime, to_tz_offset: str, from_tz_offset: str = "+00:00") -> datetime: def dt_tz_translation(dt: datetime, to_tz_offset: str, from_tz_offset: str = "+00:00") -> datetime:
to_shh, to_mm = split_offset(to_tz_offset) to_shh, to_mm = split_offset(to_tz_offset)
from_shh, from_mm = split_offset(from_tz_offset) from_shh, from_mm = split_offset(from_tz_offset)
@ -647,6 +657,10 @@ def get_local_tzname_iana():
def get_local_tz_offset(): def get_local_tz_offset():
from tww.tokenizer import custom_tz
global custom_tz
if custom_tz and not custom_tz.get('is_local'):
return custom_tz.get('tz_offset')
now = datetime.now(tzlocal()) now = datetime.now(tzlocal())
if now.tzinfo._isdst(now): if now.tzinfo._isdst(now):
return format_offset_from_timedelta(now.tzinfo._dst_offset) return format_offset_from_timedelta(now.tzinfo._dst_offset)

View File

@ -3,7 +3,8 @@ import json
import locale import locale
import re import re
import logging import logging
from datetime import datetime from datetime import datetime, timedelta
import calendar
from pygments import highlight, lexers, formatters from pygments import highlight, lexers, formatters
from scalpl import Cut from scalpl import Cut
@ -16,6 +17,7 @@ from tww.lib import resolve_timezone, dateparser_parse_dt, get_utcnow, get_s_sin
from tww.common import logger from tww.common import logger
custom_locale = resolve_locale() custom_locale = resolve_locale()
custom_tz = None
r_generic = re.compile('(.*)', flags=re.IGNORECASE) r_generic = re.compile('(.*)', flags=re.IGNORECASE)
r_time_in_epoch_s_now = re.compile('(?:time since epoch|seconds since epoch)', flags=re.IGNORECASE) r_time_in_epoch_s_now = re.compile('(?:time since epoch|seconds since epoch)', flags=re.IGNORECASE)
@ -28,6 +30,7 @@ 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\s*(.*)', flags=re.IGNORECASE) r_time_since = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*since\s*(.*)', flags=re.IGNORECASE)
r_time_until = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*until\s*(.*)', flags=re.IGNORECASE) r_time_until = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*until\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_between = re.compile('(?:time|year|month|week|day|hour|minute|second)?(?:s)?\s*between\s*(.*)\s*and\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_plus = re.compile('(.*)\s*(?:plus|\+|after|from)\s*(.*)', flags=re.IGNORECASE)
r_time_minus = re.compile('(.*)\s*(?:minus|\-)\s*(.*)', flags=re.IGNORECASE) r_time_minus = re.compile('(.*)\s*(?:minus|\-)\s*(.*)', flags=re.IGNORECASE)
r_time_before = re.compile('(.*)\s*(?:before|\-)\s*(.*)', flags=re.IGNORECASE) r_time_before = re.compile('(.*)\s*(?:before|\-)\s*(.*)', flags=re.IGNORECASE)
@ -43,13 +46,11 @@ r_timezone_translation = re.compile('(.*)?\s(?:in|to)\s(.*)', flags=re.IGNORECAS
r_timezone_translation_in_to = re.compile('(.*)(?:in)\s(.*)\s(?:to)\s(.*)', flags=re.IGNORECASE) r_timezone_translation_in_to = re.compile('(.*)(?:in)\s(.*)\s(?:to)\s(.*)', flags=re.IGNORECASE)
r_hour_minute_timezone = re.compile('((?:[0[0-9]|1[0-9]|2[0-3]):[0-5][0-9])?\s(.*)', flags=re.IGNORECASE) r_hour_minute_timezone = re.compile('((?:[0[0-9]|1[0-9]|2[0-3]):[0-5][0-9])?\s(.*)', flags=re.IGNORECASE)
r_timezone = re.compile('(.*)\s(?:timezone|timezones|tz)', flags=re.IGNORECASE) r_timezone = re.compile('(.*)\s(?:timezone|timezones|tz)', flags=re.IGNORECASE)
r_calendar_year = re.compile('(?:cal year|calendar year)?\s*(.*)', flags=re.IGNORECASE)
r_calendar_month = re.compile('(?:calendar|cal|month|cal month|calendar month)?\s*(.*)', flags=re.IGNORECASE)
r_timezone_2 = re.compile('(?:timezone in|timezones in|tz in|timezone|timezones|tz)\s(.*)?', flags=re.IGNORECASE) r_timezone_2 = re.compile('(?:timezone in|timezones in|tz in|timezone|timezones|tz)\s(.*)?', flags=re.IGNORECASE)
def handler_time(dt_s):
return dateparser_parse_dt(dt_s)
def handler_time_now_local(): def handler_time_now_local():
return get_local_now() return get_local_now()
@ -68,6 +69,15 @@ def dt_normalize(start_dt, end_dt) -> (datetime, datetime):
return start_dt, end_dt return start_dt, end_dt
def handler_tz_diff(start_tz_s: str, end_tz_s: str) -> dict:
start_tz = resolve_timezone(start_tz_s)
end_tz = resolve_timezone(end_tz_s)
diff = timedelta(seconds=start_tz["tz_offset_seconds"] - end_tz["tz_offset_seconds"])
return dict(start=start_tz,
end=end_tz,
diff=td_pretty(diff))
def handler_time_diff(start_dt, end_dt) -> dict: def handler_time_diff(start_dt, end_dt) -> dict:
start_dt, end_dt = dt_normalize(start_dt, end_dt) start_dt, end_dt = dt_normalize(start_dt, end_dt)
diff = start_dt - end_dt diff = start_dt - end_dt
@ -182,10 +192,19 @@ def handler_time_before(td: str, dt_s: str):
return dt - td return dt - td
def handler_calendar(dt_s: str):
dt = dateparser_parse_dt(dt_s)
return {
"month": calendar.month(int(dt.strftime("%Y")), int(dt.strftime("%m"))),
"year": calendar.calendar(int(dt.strftime("%Y"))),
}
QUERY_TYPE_DT_TR = "datetime_translation" QUERY_TYPE_DT_TR = "datetime_translation"
QUERY_TYPE_DT = "datetime_details" QUERY_TYPE_DT = "datetime_details"
QUERY_TYPE_TZ = "timezone" QUERY_TYPE_TZ = "timezone"
QUERY_TYPE_TD = "timedelta" QUERY_TYPE_TD = "timedelta"
QUERY_TYPE_CAL = "calendar"
h_default = '' h_default = ''
h_unix_s = 'dt->unix_s' h_unix_s = 'dt->unix_s'
@ -196,21 +215,24 @@ h_translation = 'dt->iso8601_full'
h_default_dt = 'dt->iso8601_full' h_default_dt = 'dt->iso8601_full'
h_default_td = 'timedelta->diff->duration_human' h_default_td = 'timedelta->diff->duration_human'
h_day_of_week = 'dt->locale_day_of_week' h_day_of_week = 'dt->locale_day_of_week'
h_cal_year = 'cal->year'
h_cal_month = 'cal->month'
regex_handlers = [ regex_handlers = [
(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_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_s_now, handler_time_now_utc, QUERY_TYPE_DT, h_unix_s),
(r_time_in_epoch_s2, handler_time, QUERY_TYPE_DT, h_unix_s), (r_time_in_epoch_s2, handler_generic_parser, QUERY_TYPE_DT, h_unix_s),
(r_time_in_epoch_s3, handler_time, QUERY_TYPE_DT, h_unix_s), (r_time_in_epoch_s3, handler_generic_parser, QUERY_TYPE_DT, h_unix_s),
(r_time_in_epoch_ms_now, handler_time_now_local, QUERY_TYPE_DT, h_unix_ms), (r_time_in_epoch_ms_now, handler_time_now_local, QUERY_TYPE_DT, h_unix_ms),
(r_time_in_epoch_ms_now, handler_time_now_utc, QUERY_TYPE_DT, h_unix_ms), (r_time_in_epoch_ms_now, handler_time_now_utc, QUERY_TYPE_DT, h_unix_ms),
(r_time_in_epoch_ms2, handler_time, QUERY_TYPE_DT, h_unix_ms), (r_time_in_epoch_ms2, handler_generic_parser, QUERY_TYPE_DT, h_unix_ms),
(r_time_in_epoch_ms3, handler_time, QUERY_TYPE_DT, h_unix_ms), (r_time_in_epoch_ms3, handler_generic_parser, QUERY_TYPE_DT, h_unix_ms),
(r_timezone_translation, handler_timezone_translation, QUERY_TYPE_DT_TR, h_translation), (r_timezone_translation, handler_timezone_translation, QUERY_TYPE_DT_TR, h_translation),
(r_timezone_translation_in_to, handler_timezone_translation_in_to, QUERY_TYPE_DT_TR, h_translation), (r_timezone_translation_in_to, handler_timezone_translation_in_to, QUERY_TYPE_DT_TR, h_translation),
(r_time_since, handler_time_since_until, QUERY_TYPE_TD, h_default_td), (r_time_since, handler_time_since_until, QUERY_TYPE_TD, h_default_td),
(r_time_until, handler_time_since_until, QUERY_TYPE_TD, h_default_td), (r_time_until, handler_time_since_until, QUERY_TYPE_TD, h_default_td),
(r_time_between, handler_time_diff, QUERY_TYPE_TD, h_default_td), (r_time_between, handler_time_diff, QUERY_TYPE_TD, h_default_td),
(r_tz_between, handler_tz_diff, QUERY_TYPE_TD, h_default_td),
(r_time_plus, handler_time_plus, QUERY_TYPE_DT, h_default_dt), (r_time_plus, handler_time_plus, QUERY_TYPE_DT, h_default_dt),
(r_time_minus, handler_time_minus, QUERY_TYPE_DT, h_default_dt), (r_time_minus, handler_time_minus, QUERY_TYPE_DT, h_default_dt),
(r_time_before, handler_time_before, QUERY_TYPE_DT, h_default_dt), (r_time_before, handler_time_before, QUERY_TYPE_DT, h_default_dt),
@ -226,6 +248,8 @@ regex_handlers = [
(r_timezone, handler_timezone, QUERY_TYPE_TZ, h_tz_offset), (r_timezone, handler_timezone, QUERY_TYPE_TZ, h_tz_offset),
(r_timezone_2, handler_timezone, QUERY_TYPE_TZ, h_tz_offset), (r_timezone_2, handler_timezone, QUERY_TYPE_TZ, h_tz_offset),
(r_hour_minute_timezone, handler_timezone_creation, QUERY_TYPE_DT_TR, h_translation), (r_hour_minute_timezone, handler_timezone_creation, QUERY_TYPE_DT_TR, h_translation),
(r_calendar_year, handler_calendar, QUERY_TYPE_CAL, h_cal_year),
(r_calendar_month, handler_calendar, QUERY_TYPE_CAL, h_cal_month),
(r_generic, handler_generic_parser, QUERY_TYPE_DT, h_default_dt), (r_generic, handler_generic_parser, QUERY_TYPE_DT, h_default_dt),
] ]
@ -351,7 +375,7 @@ def resolve_query(query, allowed_queries=None):
} }
solutions = resolve_query_type(query) solutions = resolve_query_type(query)
if not allowed_queries: if not allowed_queries:
allowed_queries = [QUERY_TYPE_DT, QUERY_TYPE_DT_TR, QUERY_TYPE_TD, QUERY_TYPE_TZ] allowed_queries = [QUERY_TYPE_DT, QUERY_TYPE_DT_TR, QUERY_TYPE_TD, QUERY_TYPE_TZ, QUERY_TYPE_CAL]
for sol_id, solution in enumerate(solutions): for sol_id, solution in enumerate(solutions):
element = {} element = {}
handler, results, query_type, hi = solution handler, results, query_type, hi = solution
@ -372,6 +396,8 @@ def resolve_query(query, allowed_queries=None):
element["tz"] = results element["tz"] = results
elif query_type == QUERY_TYPE_TD: elif query_type == QUERY_TYPE_TD:
element["timedelta"] = results element["timedelta"] = results
elif query_type == QUERY_TYPE_CAL:
element["cal"] = results
rv["solutions"].append(element) rv["solutions"].append(element)
except Exception as e: except Exception as e:
continue continue
@ -409,6 +435,7 @@ def parse_args():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('query', nargs='*', default="", help="freeform") parser.add_argument('query', nargs='*', default="", help="freeform")
parser.add_argument('--locale', dest='locale') parser.add_argument('--locale', dest='locale')
parser.add_argument('--tz', dest='tz', default='local')
parser.add_argument('--handlers', dest='handlers') parser.add_argument('--handlers', dest='handlers')
parser.add_argument('--show', dest='show') parser.add_argument('--show', dest='show')
parser.add_argument('--full', dest='full', action='store_true') parser.add_argument('--full', dest='full', action='store_true')
@ -425,7 +452,9 @@ def setup_logging_level(debug=False):
def main(args): def main(args):
global custom_locale global custom_locale
global custom_tz
custom_locale = resolve_locale(args.locale) custom_locale = resolve_locale(args.locale)
custom_tz = resolve_timezone(args.tz)
if custom_locale: if custom_locale:
logger.debug("Locale understood as: {}".format(custom_locale)) logger.debug("Locale understood as: {}".format(custom_locale))
query = ' '.join(args.query) query = ' '.join(args.query)