Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/iso8601/iso8601.py
7801 views
"""ISO 8601 date time string parsing12Basic usage:3>>> import iso86014>>> iso8601.parse_date("2007-01-25T12:00:00Z")5datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.Utc ...>)6>>>78"""910import datetime11import re12import typing13from decimal import Decimal1415__all__ = ["parse_date", "ParseError", "UTC", "FixedOffset"]1617# Adapted from http://delete.me.uk/2005/03/iso8601.html18ISO8601_REGEX = re.compile(19r"""20(?P<year>[0-9]{4})21(22(23(-(?P<monthdash>[0-9]{1,2}))24|25(?P<month>[0-9]{2})26(?!$) # Don't allow YYYYMM27)28(29(30(-(?P<daydash>[0-9]{1,2}))31|32(?P<day>[0-9]{2})33)34(35(36(?P<separator>[ T])37(?P<hour>[0-9]{2})38(:{0,1}(?P<minute>[0-9]{2})){0,1}39(40:{0,1}(?P<second>[0-9]{1,2})41([.,](?P<second_fraction>[0-9]+)){0,1}42){0,1}43(?P<timezone>44Z45|46(47(?P<tz_sign>[-+])48(?P<tz_hour>[0-9]{2})49:{0,1}50(?P<tz_minute>[0-9]{2}){0,1}51)52){0,1}53){0,1}54)55){0,1} # YYYY-MM56){0,1} # YYYY only57$58""",59re.VERBOSE,60)616263class ParseError(ValueError):64"""Raised when there is a problem parsing a date string"""656667UTC = datetime.timezone.utc686970def FixedOffset(71offset_hours: float, offset_minutes: float, name: str72) -> datetime.timezone:73return datetime.timezone(74datetime.timedelta(hours=offset_hours, minutes=offset_minutes), name75)767778def parse_timezone(79matches: typing.Dict[str, str],80default_timezone: typing.Optional[datetime.timezone] = UTC,81) -> typing.Optional[datetime.timezone]:82"""Parses ISO 8601 time zone specs into tzinfo offsets"""83tz = matches.get("timezone", None)84if tz == "Z":85return UTC86# This isn't strictly correct, but it's common to encounter dates without87# timezones so I'll assume the default (which defaults to UTC).88# Addresses issue 4.89if tz is None:90return default_timezone91sign = matches.get("tz_sign", None)92hours = int(matches.get("tz_hour", 0))93minutes = int(matches.get("tz_minute", 0))94description = f"{sign}{hours:02d}:{minutes:02d}"95if sign == "-":96hours = -hours97minutes = -minutes98return FixedOffset(hours, minutes, description)99100101def parse_date(102datestring: str, default_timezone: typing.Optional[datetime.timezone] = UTC103) -> datetime.datetime:104"""Parses ISO 8601 dates into datetime objects105106The timezone is parsed from the date string. However it is quite common to107have dates without a timezone (not strictly correct). In this case the108default timezone specified in default_timezone is used. This is UTC by109default.110111:param datestring: The date to parse as a string112:param default_timezone: A datetime tzinfo instance to use when no timezone113is specified in the datestring. If this is set to114None then a naive datetime object is returned.115:returns: A datetime.datetime instance116:raises: ParseError when there is a problem parsing the date or117constructing the datetime instance.118119"""120try:121m = ISO8601_REGEX.match(datestring)122except Exception as e:123raise ParseError(e)124125if not m:126raise ParseError(f"Unable to parse date string {datestring!r}")127128# Drop any Nones from the regex matches129# TODO: check if there's a way to omit results in regexes130groups: typing.Dict[str, str] = {131k: v for k, v in m.groupdict().items() if v is not None132}133134try:135return datetime.datetime(136year=int(groups.get("year", 0)),137month=int(groups.get("month", groups.get("monthdash", 1))),138day=int(groups.get("day", groups.get("daydash", 1))),139hour=int(groups.get("hour", 0)),140minute=int(groups.get("minute", 0)),141second=int(groups.get("second", 0)),142microsecond=int(143Decimal(f"0.{groups.get('second_fraction', 0)}") * Decimal("1000000.0")144),145tzinfo=parse_timezone(groups, default_timezone=default_timezone),146)147except Exception as e:148raise ParseError(e)149150151