2019-12-08 22:43:49 +01:00
|
|
|
import calendar
|
|
|
|
import datetime
|
|
|
|
from dateutil import tz
|
|
|
|
|
2021-06-07 07:46:39 +02:00
|
|
|
from typing import List, Optional
|
|
|
|
from typing_extensions import Final
|
2019-12-08 22:43:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Time:
|
|
|
|
"""
|
|
|
|
Python's time stuff sucks, so this provides a sane interface to getting
|
|
|
|
standard unix timestamps at UTC timezone given various parameters.
|
|
|
|
"""
|
|
|
|
|
2023-08-19 19:41:14 +02:00
|
|
|
SECONDS_IN_SECOND: Final[int] = 1
|
2021-05-31 20:09:40 +02:00
|
|
|
SECONDS_IN_MINUTE: Final[int] = 60
|
|
|
|
SECONDS_IN_HOUR: Final[int] = 3600
|
|
|
|
SECONDS_IN_DAY: Final[int] = 86400
|
|
|
|
SECONDS_IN_WEEK: Final[int] = 604800
|
2019-12-08 22:43:49 +01:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def now() -> int:
|
|
|
|
"""
|
|
|
|
Returns the current unix timestamp in the UTC timezone.
|
|
|
|
"""
|
|
|
|
return calendar.timegm(datetime.datetime.utcnow().timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def end_of_today() -> int:
|
|
|
|
"""
|
|
|
|
Returns the unix timestamp for the end of today in UTC timezone.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
|
|
|
beginning_of_day = datetime.datetime(
|
2022-10-15 20:56:30 +02:00
|
|
|
now.year, now.month, now.day, tzinfo=tz.tzutc()
|
2019-12-08 22:43:49 +01:00
|
|
|
)
|
|
|
|
end_of_day = beginning_of_day + datetime.timedelta(days=1)
|
|
|
|
return calendar.timegm(end_of_day.timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def beginning_of_today() -> int:
|
|
|
|
"""
|
|
|
|
Returns the unix timestamp for the beginning of today in UTC timezone.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
|
|
|
beginning_of_day = datetime.datetime(
|
2022-10-15 20:56:30 +02:00
|
|
|
now.year, now.month, now.day, tzinfo=tz.tzutc()
|
2019-12-08 22:43:49 +01:00
|
|
|
)
|
|
|
|
return calendar.timegm(beginning_of_day.timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def end_of_this_week() -> int:
|
|
|
|
"""
|
|
|
|
Returns the unix timestamp for the end of this week in UTC timezone.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
2021-05-31 20:07:03 +02:00
|
|
|
this_week = now - datetime.timedelta(days=now.timetuple().tm_wday)
|
2019-12-08 22:43:49 +01:00
|
|
|
next_week = this_week + datetime.timedelta(days=7)
|
|
|
|
return calendar.timegm(next_week.timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def beginning_of_this_week() -> int:
|
|
|
|
"""
|
|
|
|
Returns the unix timestamp for the beginning of this week in UTC timezone.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
2021-05-31 20:07:03 +02:00
|
|
|
this_week = now - datetime.timedelta(days=now.timetuple().tm_wday)
|
2019-12-08 22:43:49 +01:00
|
|
|
return calendar.timegm(this_week.timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def end_of_this_month() -> int:
|
|
|
|
"""
|
|
|
|
Returns the unix timestamp for the end of this month in UTC timezone.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
|
|
|
return Time.timestamp_from_date(now.year, now.month + 1, 1)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def beginning_of_this_month() -> int:
|
|
|
|
"""
|
|
|
|
Returns the unix timestamp for the beginning of this month in UTC timezone.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
|
|
|
this_month = datetime.date(now.year, now.month, 1)
|
|
|
|
return calendar.timegm(this_month.timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def todays_date() -> List[int]:
|
|
|
|
"""
|
|
|
|
Returns a [year, month, day] list representing today's date.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
|
|
|
return [now.year, now.month, now.day]
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def yesterdays_date() -> List[int]:
|
|
|
|
"""
|
|
|
|
Returns a [year, month, day] list representing yesterday's date.
|
|
|
|
"""
|
|
|
|
now = datetime.datetime.utcnow().date()
|
|
|
|
yesterday = now - datetime.timedelta(days=1)
|
|
|
|
return [yesterday.year, yesterday.month, yesterday.day]
|
|
|
|
|
|
|
|
@staticmethod
|
2022-10-15 20:56:30 +02:00
|
|
|
def week_in_days_since_epoch(timestamp: Optional[int] = None) -> int:
|
2019-12-08 22:43:49 +01:00
|
|
|
"""
|
|
|
|
Returns the day number of the beginning of this week, where day zero is
|
|
|
|
the unix epoch at UTC timezone. So if we were one week in, this would return
|
|
|
|
7. If a timestamp is provided, returns the same value from that reverence
|
|
|
|
point instead of now.
|
|
|
|
"""
|
|
|
|
if timestamp is None:
|
|
|
|
date = datetime.datetime.utcnow().date()
|
|
|
|
else:
|
|
|
|
date = datetime.datetime.utcfromtimestamp(timestamp).date()
|
2021-05-31 20:07:03 +02:00
|
|
|
week = date - datetime.timedelta(days=date.timetuple().tm_wday)
|
2019-12-08 22:43:49 +01:00
|
|
|
return (week - datetime.date(1970, 1, 1)).days
|
|
|
|
|
|
|
|
@staticmethod
|
2022-10-15 20:56:30 +02:00
|
|
|
def days_into_year(timestamp: Optional[int] = None) -> List[int]:
|
2019-12-08 22:43:49 +01:00
|
|
|
"""
|
|
|
|
Returns a [year, days] list representing the current year, and number
|
|
|
|
of days into the current year. If a timestamp is provided, returns the
|
|
|
|
same value from that reverence point instead of now.
|
|
|
|
"""
|
|
|
|
if timestamp is None:
|
|
|
|
date = datetime.datetime.utcnow().date().timetuple()
|
|
|
|
else:
|
|
|
|
date = datetime.datetime.utcfromtimestamp(timestamp).date().timetuple()
|
2021-05-31 20:07:03 +02:00
|
|
|
return [date.tm_year, date.tm_yday]
|
2019-12-08 22:43:49 +01:00
|
|
|
|
|
|
|
@staticmethod
|
2022-10-15 20:56:30 +02:00
|
|
|
def days_into_week(timestamp: Optional[int] = None) -> int:
|
2019-12-08 22:43:49 +01:00
|
|
|
"""
|
|
|
|
Returns an integer representing the number of days into the current week
|
|
|
|
we are, with 0 = monday, 1 = tuesday, etc. If a timestamp is provided,
|
|
|
|
returns the same value from that reverence point instead of now.
|
|
|
|
"""
|
|
|
|
if timestamp is None:
|
|
|
|
date = datetime.datetime.utcnow().date().timetuple()
|
|
|
|
else:
|
|
|
|
date = datetime.datetime.utcfromtimestamp(timestamp).date().timetuple()
|
2021-05-31 20:07:03 +02:00
|
|
|
return date.tm_wday
|
2019-12-08 22:43:49 +01:00
|
|
|
|
|
|
|
@staticmethod
|
2022-10-15 20:56:30 +02:00
|
|
|
def timestamp_from_date(year: int, month: int = 1, day: int = 1) -> int:
|
2019-12-08 22:43:49 +01:00
|
|
|
"""
|
|
|
|
Given a date (either a year, year/month, or year/month/day), returns
|
|
|
|
the unix timestamp from UTC of that date. Supports out of bounds
|
|
|
|
indexing on month.
|
|
|
|
"""
|
|
|
|
while month < 1:
|
|
|
|
year = year - 1
|
|
|
|
month = month + 12
|
|
|
|
while month > 12:
|
|
|
|
year = year + 1
|
|
|
|
month = month - 12
|
|
|
|
|
2022-10-15 20:56:30 +02:00
|
|
|
date = datetime.datetime(year, month, day, tzinfo=tz.tzutc())
|
2019-12-08 22:43:49 +01:00
|
|
|
return calendar.timegm(date.timetuple())
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def date_from_timestamp(timestamp: int) -> List[int]:
|
|
|
|
"""
|
|
|
|
Returns a [year, month, day] given a UTC unix timestamp.
|
|
|
|
"""
|
|
|
|
date = datetime.datetime.utcfromtimestamp(timestamp).date()
|
|
|
|
return [date.year, date.month, date.day]
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def format(timestamp: int, formatstr: str) -> str:
|
|
|
|
"""
|
|
|
|
Returns a unix timestamp based at UTC timezone formatted as a string.
|
|
|
|
"""
|
|
|
|
return datetime.datetime.utcfromtimestamp(timestamp).strftime(formatstr)
|