_re.py
101 lines
| 2.7 KiB
| text/x-python
|
PythonLexer
Raphaël Gomès
|
r51654 | from datetime import date, datetime, time, timedelta, timezone, tzinfo | ||
from functools import lru_cache | ||||
import re | ||||
from typing import Any, Optional, Union | ||||
from ._types import ParseFloat | ||||
# E.g. | ||||
# - 00:32:00.999999 | ||||
# - 00:32:00 | ||||
_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?" | ||||
RE_NUMBER = re.compile( | ||||
r""" | ||||
0 | ||||
(?: | ||||
x[0-9A-Fa-f](?:_?[0-9A-Fa-f])* # hex | ||||
| | ||||
b[01](?:_?[01])* # bin | ||||
| | ||||
o[0-7](?:_?[0-7])* # oct | ||||
) | ||||
| | ||||
[+-]?(?:0|[1-9](?:_?[0-9])*) # dec, integer part | ||||
(?P<floatpart> | ||||
(?:\.[0-9](?:_?[0-9])*)? # optional fractional part | ||||
(?:[eE][+-]?[0-9](?:_?[0-9])*)? # optional exponent part | ||||
) | ||||
""", | ||||
flags=re.VERBOSE, | ||||
) | ||||
RE_LOCALTIME = re.compile(_TIME_RE_STR) | ||||
RE_DATETIME = re.compile( | ||||
fr""" | ||||
([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27 | ||||
(?: | ||||
[Tt ] | ||||
{_TIME_RE_STR} | ||||
(?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))? # optional time offset | ||||
)? | ||||
""", | ||||
flags=re.VERBOSE, | ||||
) | ||||
def match_to_datetime(match: "re.Match") -> Union[datetime, date]: | ||||
"""Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`. | ||||
Raises ValueError if the match does not correspond to a valid date | ||||
or datetime. | ||||
""" | ||||
( | ||||
year_str, | ||||
month_str, | ||||
day_str, | ||||
hour_str, | ||||
minute_str, | ||||
sec_str, | ||||
micros_str, | ||||
zulu_time, | ||||
offset_sign_str, | ||||
offset_hour_str, | ||||
offset_minute_str, | ||||
) = match.groups() | ||||
year, month, day = int(year_str), int(month_str), int(day_str) | ||||
if hour_str is None: | ||||
return date(year, month, day) | ||||
hour, minute, sec = int(hour_str), int(minute_str), int(sec_str) | ||||
micros = int(micros_str.ljust(6, "0")) if micros_str else 0 | ||||
if offset_sign_str: | ||||
tz: Optional[tzinfo] = cached_tz( | ||||
offset_hour_str, offset_minute_str, offset_sign_str | ||||
) | ||||
elif zulu_time: | ||||
tz = timezone.utc | ||||
else: # local date-time | ||||
tz = None | ||||
return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz) | ||||
@lru_cache(maxsize=None) | ||||
def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone: | ||||
sign = 1 if sign_str == "+" else -1 | ||||
return timezone( | ||||
timedelta( | ||||
hours=sign * int(hour_str), | ||||
minutes=sign * int(minute_str), | ||||
) | ||||
) | ||||
def match_to_localtime(match: "re.Match") -> time: | ||||
hour_str, minute_str, sec_str, micros_str = match.groups() | ||||
micros = int(micros_str.ljust(6, "0")) if micros_str else 0 | ||||
return time(int(hour_str), int(minute_str), int(sec_str), micros) | ||||
def match_to_number(match: "re.Match", parse_float: "ParseFloat") -> Any: | ||||
if match.group("floatpart"): | ||||
return parse_float(match.group()) | ||||
return int(match.group(), 0) | ||||