2024-02-25 00:38:44 +00:00
|
|
|
import datetime
|
|
|
|
|
|
|
|
DATETIME_UNITS = {
|
2024-03-31 22:55:55 +00:00
|
|
|
'second': datetime.timedelta(seconds=1),
|
|
|
|
'seconds': datetime.timedelta(seconds=1),
|
|
|
|
'minute': datetime.timedelta(minutes=1),
|
|
|
|
'minutes': datetime.timedelta(minutes=1),
|
|
|
|
'hour': datetime.timedelta(hours=1),
|
|
|
|
'hours': datetime.timedelta(hours=1),
|
|
|
|
'day': datetime.timedelta(days=1),
|
|
|
|
'days': datetime.timedelta(days=1),
|
|
|
|
'week': datetime.timedelta(days=7),
|
|
|
|
'weeks': datetime.timedelta(days=7),
|
|
|
|
'month': datetime.timedelta(days=30),
|
|
|
|
'months': datetime.timedelta(days=30),
|
|
|
|
'year': datetime.timedelta(days=365),
|
|
|
|
'years': datetime.timedelta(days=365),
|
2024-02-25 00:38:44 +00:00
|
|
|
}
|
|
|
|
|
2024-03-03 16:25:34 +00:00
|
|
|
FORMAT_DATE_HEADER = '%a, %d %b %Y %H:%M:%S GMT'
|
|
|
|
|
2024-03-31 22:55:55 +00:00
|
|
|
|
2024-02-25 00:38:44 +00:00
|
|
|
def parse_duration(text: str) -> datetime.timedelta:
|
2024-10-25 20:09:27 +00:00
|
|
|
(num_str, unit_str) = text.split(' ')
|
2024-11-08 20:42:33 +00:00
|
|
|
num = float(num_str)
|
2024-10-25 20:09:27 +00:00
|
|
|
unit = DATETIME_UNITS[unit_str]
|
2024-02-25 00:38:44 +00:00
|
|
|
return unit * num
|
2024-03-03 16:25:34 +00:00
|
|
|
|
2024-05-09 14:59:56 +00:00
|
|
|
|
2024-05-09 14:58:09 +00:00
|
|
|
def parse_response_datetime(response) -> datetime.datetime:
|
2024-07-25 11:06:05 +00:00
|
|
|
return datetime.datetime.strptime(
|
2024-08-25 18:50:03 +00:00
|
|
|
response.headers['Date'],
|
|
|
|
FORMAT_DATE_HEADER,
|
2024-07-25 11:06:05 +00:00
|
|
|
).replace(tzinfo=datetime.UTC)
|
|
|
|
|
2024-05-09 14:59:56 +00:00
|
|
|
|
2024-10-09 12:24:14 +00:00
|
|
|
NOW = datetime.datetime.now(datetime.UTC)
|
|
|
|
LOCAL_TIMEZONE = NOW.astimezone().tzinfo
|
2024-09-01 14:55:45 +00:00
|
|
|
|
|
|
|
|
2024-10-03 21:24:12 +00:00
|
|
|
def try_parse(text: str, fmt: str) -> datetime.datetime | None:
|
2024-09-01 14:55:45 +00:00
|
|
|
try:
|
2024-10-25 20:24:33 +00:00
|
|
|
time = datetime.datetime.strptime(text, fmt) # noqa: DTZ007
|
2024-09-01 14:55:45 +00:00
|
|
|
if time.tzinfo is None:
|
|
|
|
time = time.replace(tzinfo=LOCAL_TIMEZONE)
|
2024-10-25 20:24:33 +00:00
|
|
|
except ValueError:
|
2024-09-01 14:55:45 +00:00
|
|
|
time = None
|
|
|
|
return time
|
2024-08-26 22:31:44 +00:00
|
|
|
|
|
|
|
|
2024-05-09 14:58:09 +00:00
|
|
|
def parse_time(text: str) -> datetime.datetime:
|
|
|
|
text = text.replace('\n', ' ')
|
|
|
|
text = text.strip()
|
2024-09-01 14:55:45 +00:00
|
|
|
|
|
|
|
time = try_parse(text, '%d %b %Y %I:%M:%S %p')
|
|
|
|
time = time or try_parse(text, '%d %b, %Y @ %I:%M%p')
|
2024-10-09 12:24:14 +00:00
|
|
|
if time is None and (m := try_parse(text, '%d %b @ %I:%M%p')):
|
2024-10-13 13:20:30 +00:00
|
|
|
time = m.replace(year=NOW.year)
|
2024-10-09 12:24:14 +00:00
|
|
|
|
2024-10-25 20:09:27 +00:00
|
|
|
if time is None:
|
|
|
|
msg = 'Unknown format: ' + text
|
|
|
|
raise RuntimeError(msg)
|
2024-10-09 12:24:14 +00:00
|
|
|
|
|
|
|
if time.tzinfo is None:
|
2024-10-13 13:20:30 +00:00
|
|
|
time = time.replace(tzinfo=LOCAL_TIMEZONE)
|
2024-09-01 14:55:45 +00:00
|
|
|
|
2024-10-25 20:09:27 +00:00
|
|
|
if time.tzinfo is None:
|
|
|
|
msg = 'Could not parse timezone: ' + text
|
|
|
|
raise RuntimeError(msg)
|
2024-11-24 17:44:04 +00:00
|
|
|
return time.astimezone(datetime.UTC)
|
2024-05-09 14:58:09 +00:00
|
|
|
|
2024-05-09 14:59:56 +00:00
|
|
|
|
2024-05-09 14:58:09 +00:00
|
|
|
def parse_date(text: str) -> datetime.date:
|
2024-10-25 19:29:44 +00:00
|
|
|
text = text.strip()
|
|
|
|
if dt := try_parse(text, '%d %B %Y'):
|
|
|
|
return dt.date()
|
|
|
|
if dt := try_parse(text, '%b %d, %Y'):
|
|
|
|
return dt.date()
|
|
|
|
if dt := try_parse(text, '%B %d, %Y'):
|
|
|
|
return dt.date()
|
2024-11-08 20:42:33 +00:00
|
|
|
if dt := try_parse(text, '%b %d'):
|
|
|
|
return dt.date()
|
2024-10-25 20:09:27 +00:00
|
|
|
msg = 'Unknown format: ' + text
|
|
|
|
raise RuntimeError(msg)
|