added time library
This commit is contained in:
parent
65b24f5172
commit
e4eafb5dd8
263
src/tww/time_lib.py
Normal file
263
src/tww/time_lib.py
Normal file
@ -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....
|
||||||
|
|
||||||
|
"<TIME>"
|
||||||
|
"================="
|
||||||
|
"2019-12-12T03:14:15" # ISO-8601 Naive time
|
||||||
|
"2019-12-12T03:14:15+0000" # ISO-8601 Timezone aware
|
||||||
|
"in 3 hours" # Time:relative:future
|
||||||
|
"5 days ago" # Time:relative:past
|
||||||
|
|
||||||
|
"<TIME> IN <WHEREVER>"
|
||||||
|
"================="
|
||||||
|
"Now in epoch" # Absolute:Epoch
|
||||||
|
"Now in New York" # Location:City
|
||||||
|
"Now in Bulgaria" # Location:Country
|
||||||
|
"Now in USA" # Location:Country - multiple timezones
|
||||||
|
"Now in CET" # Timezone:Abbreviation - https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations
|
||||||
|
"Now in Europe/Zurich" # Timezone:IANA - tz db, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones, timezone database
|
||||||
|
"Now in Alfa" # Timezone:Military - https://en.wikipedia.org/wiki/List_of_military_time_zones
|
||||||
|
"Now in +02:00" # Timezone:Offset - https://en.wikipedia.org/wiki/List_of_UTC_time_offsets
|
||||||
|
|
||||||
|
"<TIMEDELTA>"
|
||||||
|
"================="
|
||||||
|
"Time since <TIME>" # Timedelta:past
|
||||||
|
"Time until <TIME>" # Timedelta:future
|
||||||
|
|
||||||
|
# strptime: str -> datetime
|
||||||
|
# strftime: datetime -> str
|
||||||
|
|
||||||
|
# %Y Year with century as a decimal number.
|
||||||
|
# %m Month as a decimal number [01,12].
|
||||||
|
# %d Day of the month as a decimal number [01,31].
|
||||||
|
# %H Hour (24-hour clock) as a decimal number [00,23].
|
||||||
|
# %M Minute as a decimal number [00,59].
|
||||||
|
# %S Second as a decimal number [00,61].
|
||||||
|
# %z Time zone offset from UTC.
|
||||||
|
# %a Locale's abbreviated weekday name.
|
||||||
|
# %A Locale's full weekday name.
|
||||||
|
# %b Locale's abbreviated month name.
|
||||||
|
# %B Locale's full month name.
|
||||||
|
# %c Locale's appropriate date and time representation.
|
||||||
|
# %I Hour (12-hour clock) as a decimal number [01,12].
|
||||||
|
# %p Locale's equivalent of either AM or PM.
|
||||||
|
|
||||||
|
# https://docs.python.org/3/library/datetime.html
|
||||||
|
#
|
||||||
|
# %a Weekday as locale’s abbreviated name. "Sun, Mon, …, Sat (en_US); So, Mo, …, Sa (de_DE)
|
||||||
|
# %A Weekday as locale’s full name. Sunday, Monday, …, Saturday (en_US); Sonntag, Montag, …, Samstag (de_DE)
|
||||||
|
# %w Weekday as a decimal number, where 0 is Sunday and 6 is Saturday. 0, 1, …, 6
|
||||||
|
# %d Day of the month as a zero-padded decimal number. 01, 02, …, 31
|
||||||
|
# %b Month as locale’s abbreviated name. Jan, Feb, …, Dec (en_US); Jan, Feb, …, Dez (de_DE)
|
||||||
|
# %B Month as locale’s full name. January, February, …, December (en_US); Januar, Februar, …, Dezember (de_DE)
|
||||||
|
# %m Month as a zero-padded decimal number. 01, 02, …, 12
|
||||||
|
# %y Year without century as a zero-padded decimal number. 00, 01, …, 99
|
||||||
|
# %Y Year with century as a decimal number. 0001, 0002, …, 2013, 2014, …, 9998, 9999
|
||||||
|
# %H Hour (24-hour clock) as a zero-padded decimal number. 00, 01, …, 23
|
||||||
|
# %I Hour (12-hour clock) as a zero-padded decimal number. 01, 02, …, 12
|
||||||
|
# %p Locale’s equivalent of either AM or PM. AM, PM (en_US); am, pm (de_DE)
|
||||||
|
# %M Minute as a zero-padded decimal number. 00, 01, …, 59
|
||||||
|
# %S Second as a zero-padded decimal number. 00, 01, …, 59
|
||||||
|
# %f Microsecond as a decimal number, zero-padded on the left. 000000, 000001, …, 999999
|
||||||
|
# %z UTC offset in the form ±HHMM[SS[.ffffff]]
|
||||||
|
# (empty string if the object is naive). (empty), +0000, -0400, +1030, +063415, -030712.345216
|
||||||
|
# %Z Time zone name (empty string if the object is naive). (empty), UTC, EST, CST
|
||||||
|
# %j Day of the year as a zero-padded decimal number. 001, 002, …, 366
|
||||||
|
# %U Week number of the year (Sunday as the first day of the week)
|
||||||
|
# as a zero padded decimal number. All days in a new year
|
||||||
|
# preceding the first Sunday are considered to be in week 0. 00, 01, …, 53
|
||||||
|
# %W Week number of the year (Monday as the first day of the week)
|
||||||
|
# as a decimal number. All days in a new year preceding the
|
||||||
|
# first Monday are considered to be in week 0. 00, 01, …, 53
|
||||||
|
# %c Locale’s appropriate date and time representation. Tue Aug 16 21:30:00 1988 (en_US); Di 16 Aug 21:30:00 1988 (de_DE)
|
||||||
|
# %x Locale’s appropriate date representation. 08/16/1988 (en_US); 16.08.1988 (de_DE)
|
||||||
|
# %X Locale’s appropriate time representation. 21:30:00 (en_US); 21:30:00 (de_DE)
|
||||||
|
# %% A literal '%' character. %
|
Loading…
Reference in New Issue
Block a user