import datetime DATETIME_UNITS = { '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), } FORMAT_DATE_HEADER = '%a, %d %b %Y %H:%M:%S GMT' FORMAT_DAY_MONTH_YEAR = '%d %B %Y' def parse_duration(text: str) -> datetime.timedelta: (num, unit) = text.split(' ') num = int(num) unit = DATETIME_UNITS[unit] return unit * num def parse_response_datetime(response) -> datetime.datetime: return datetime.datetime.strptime( response.headers['Date'], FORMAT_DATE_HEADER, ).replace(tzinfo=datetime.UTC) NOW = datetime.datetime.now(datetime.UTC) LOCAL_TIMEZONE = NOW.astimezone().tzinfo def try_parse(text: str, fmt: str) -> datetime.datetime | None: try: time = datetime.datetime.strptime(text, fmt) if time.tzinfo is None: time = time.replace(tzinfo=LOCAL_TIMEZONE) except: time = None return time def parse_time(text: str) -> datetime.datetime: text = text.replace('\n', ' ') text = text.strip() time = try_parse(text, '%d %b %Y %I:%M:%S %p') time = time or try_parse(text, '%d %b, %Y @ %I:%M%p') if time is None and (m := try_parse(text, '%d %b @ %I:%M%p')): time = m.replace(year = NOW.year) assert time is not None, 'Could not parse format' if time.tzinfo is None: time = time.replace(tzinfo=LOCAL_TIMEZONE ) assert time.tzinfo is not None, time return time def parse_date(text: str) -> datetime.date: return datetime.datetime.strptime( text.strip(), FORMAT_DAY_MONTH_YEAR, ).date()