From e4eafb5dd8a317a312605539ecd884366d7b9b89 Mon Sep 17 00:00:00 2001 From: Daniel Tsvetkov Date: Thu, 12 Dec 2019 17:01:33 +0100 Subject: [PATCH] added time library --- src/tww/time_lib.py | 263 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 src/tww/time_lib.py diff --git a/src/tww/time_lib.py b/src/tww/time_lib.py new file mode 100644 index 0000000..20dcb82 --- /dev/null +++ b/src/tww/time_lib.py @@ -0,0 +1,263 @@ +import datetime +import os +from datetime import datetime, timedelta + +import pytz +from dateparser import parse as parse_dt +from dateparser.timezone_parser import StaticTzInfo +from dateutil.tz import tzlocal, gettz +from tww import parse_query, solve_query, TZ_OFFSETS + + +def tzinfo_from_offset(offset): + if ':' in offset: + ''.join(offset.split(':')) + tznames = TZ_OFFSETS.get(offset) + if not tznames: + return pytz.timezone("utc") + for tzname in tznames: + if tzname.startswith('Etc/GMT'): + return pytz.timezone(tzname) + return pytz.timezone(tznames[0]) + + +def time_ago(date=None, diff=None): + """ + Get a datetime object, timedelta object or a int() Epoch timestamp and return a + pretty string like 'an hour ago', 'Yesterday', '3 months ago', + 'just now', etc + Modified from: http://stackoverflow.com/a/1551394/141084 + """ + now = get_utcnow() + if not date: + if diff: + diff = timedelta(seconds=diff) + else: + diff = now - now + else: + if type(date) is str: + parsed_dt = parse_dt(date) + if parsed_dt.tzinfo is not None: + now = get_utcnow(tzaware=True) + diff = now - parsed_dt + elif type(date) is timedelta: + diff = date + elif type(date) is int: + diff = now - datetime.fromtimestamp(date) + elif isinstance(date, datetime): + if date.tzinfo is not None: + now = get_utcnow(tzaware=True) + diff = now - date + else: + raise ValueError('invalid date %s of type %s' % (date, type(date))) + + sign = '' + if diff.days < 0: + diff = -diff + sign = '-' + second_diff = diff.seconds + day_diff = diff.days + + if day_diff == 0: + if second_diff < 60: + return str("{}{:02d}".format(sign, second_diff)) + seconds = second_diff % 60 + if second_diff < 3600: + min_diff = second_diff // 60 + return str("{}{:02d}:{:02d}".format(sign, min_diff, seconds)) + minutes = second_diff % 3600 // 60 + if second_diff < 86400: + hrs_diff = second_diff // 3600 + return str("{}{:02d}:{:02d}:{:02d}".format(sign, hrs_diff, minutes, seconds)) + seconds = second_diff % 60 + minutes = second_diff % 3600 // 60 + hours = second_diff // 3600 + if day_diff < 365: + if day_diff < 30: + return str("{}{}d{:02d}:{:02d}:{:02d}".format(sign, day_diff, hours, minutes, seconds)) + months, days = day_diff // 30, day_diff % 30 + return str("{}{}m{}d{:02d}:{:02d}:{:02d}".format(sign, months, days, hours, minutes, seconds)) + years = day_diff // 365 + days = day_diff % 365 + months, days = days // 30, days % 30 + return str("{}{}y{}m{}d{:02d}:{:02d}:{:02d}".format(sign, years, months, days, hours, minutes, seconds)) + + +def query_to_dt(query): + human_dt, human_tz_loc = parse_query(query) + return solve_query(human_dt, human_tz_loc) + + +# ================== +def dateparser_parse_dt(s: str): + print("Dateparser query: {}".format(s)) + parsed = parse_dt(s) + print("Dateparser parsed query: {}".format(parsed)) + return parsed + + +def get_utcnow(tzaware: bool = False): + if tzaware: + return datetime.utcnow().replace(tzinfo=pytz.UTC) + return datetime.utcnow() + + +def dt_tz_translation(dt: datetime, to_tz: str, from_tz: str = "+00:00"): + if ':' in to_tz: + to_shh, to_mm = to_tz.split(':') + else: + to_shh, to_mm = to_tz[:-2], to_tz[-2:] + if ':' in from_tz: + from_shh, from_mm = from_tz.split(':') + else: + from_shh, from_mm = from_tz[:-2], to_tz[-2:] + tzinfo = tzinfo_from_offset(to_tz) + if dt.tzinfo: + return dt.astimezone(tzinfo) + r_dt = dt + timedelta(hours=int(to_shh), minutes=int(to_mm)) - timedelta(hours=int(from_shh), minutes=int(from_mm)) + r_dt.replace(tzinfo=tzinfo) + return r_dt + + +def format_offset_from_timedelta(tdelta: timedelta): + sign = "+" if tdelta.seconds >= 0 else "-" + h = tdelta.seconds // 3600 + m = (tdelta.seconds // 60) - h * 60 + return "{}{:02d}:{:02d}".format(sign, h, m) + + +def get_local_tzname_iana(): + return '/'.join(os.path.realpath(gettz()._filename).split('/')[-2:]) + + +def get_local_tz_offset(): + return format_offset_from_timedelta(datetime.now(tzlocal()).tzinfo._std_offset) + + +def tzname_to_tz_offset(tzname_iana: str): + return format_offset_from_timedelta(pytz.timezone(tzname_iana).utcoffset(get_utcnow())) + + +def get_dt_tz_offset(dt: datetime): + if dt.tzinfo is not None: + if type(dt.tzinfo) is StaticTzInfo: + tzoffset = dt.tzinfo._StaticTzInfo__offset + else: + tzoffset = dt.tzinfo._utcoffset + return format_offset_from_timedelta(tzoffset) + return "+00:00" + + +def get_seconds_since_epoch(dt): + return int(dt.timestamp()) + + +def epoch_to_dt(seconds): + return datetime.fromtimestamp(seconds) + + +def time_to_emoji(dt): + seconds = get_seconds_since_epoch(dt) + a = int((seconds / 900 - 3) / 2 % 24) + return chr(128336 + a // 2 + a % 2 * 12) + + +if __name__ == "__main__": + print(get_local_tzname_iana()) + + print(get_local_tz_offset()) + + print(tzname_to_tz_offset( + get_local_tzname_iana() + )) + + print(dt_tz_translation( + get_utcnow(), + get_local_tz_offset() + )) + + print(time_to_emoji( + dt_tz_translation( + get_utcnow(), + get_local_tz_offset() + ) + )) + + print(get_seconds_since_epoch( + dateparser_parse_dt("2019-12-11 15:53:40+0000") + )) + + # QUERIES.... + + "