17db96d56Sopenharmony_ci# SPDX-License-Identifier: MIT
27db96d56Sopenharmony_ci# SPDX-FileCopyrightText: 2021 Taneli Hukkinen
37db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement.
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_cifrom __future__ import annotations
67db96d56Sopenharmony_ci
77db96d56Sopenharmony_cifrom datetime import date, datetime, time, timedelta, timezone, tzinfo
87db96d56Sopenharmony_cifrom functools import lru_cache
97db96d56Sopenharmony_ciimport re
107db96d56Sopenharmony_cifrom typing import Any
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_cifrom ._types import ParseFloat
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_ci# E.g.
157db96d56Sopenharmony_ci# - 00:32:00.999999
167db96d56Sopenharmony_ci# - 00:32:00
177db96d56Sopenharmony_ci_TIME_RE_STR = r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?"
187db96d56Sopenharmony_ci
197db96d56Sopenharmony_ciRE_NUMBER = re.compile(
207db96d56Sopenharmony_ci    r"""
217db96d56Sopenharmony_ci0
227db96d56Sopenharmony_ci(?:
237db96d56Sopenharmony_ci    x[0-9A-Fa-f](?:_?[0-9A-Fa-f])*   # hex
247db96d56Sopenharmony_ci    |
257db96d56Sopenharmony_ci    b[01](?:_?[01])*                 # bin
267db96d56Sopenharmony_ci    |
277db96d56Sopenharmony_ci    o[0-7](?:_?[0-7])*               # oct
287db96d56Sopenharmony_ci)
297db96d56Sopenharmony_ci|
307db96d56Sopenharmony_ci[+-]?(?:0|[1-9](?:_?[0-9])*)         # dec, integer part
317db96d56Sopenharmony_ci(?P<floatpart>
327db96d56Sopenharmony_ci    (?:\.[0-9](?:_?[0-9])*)?         # optional fractional part
337db96d56Sopenharmony_ci    (?:[eE][+-]?[0-9](?:_?[0-9])*)?  # optional exponent part
347db96d56Sopenharmony_ci)
357db96d56Sopenharmony_ci""",
367db96d56Sopenharmony_ci    flags=re.VERBOSE,
377db96d56Sopenharmony_ci)
387db96d56Sopenharmony_ciRE_LOCALTIME = re.compile(_TIME_RE_STR)
397db96d56Sopenharmony_ciRE_DATETIME = re.compile(
407db96d56Sopenharmony_ci    rf"""
417db96d56Sopenharmony_ci([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])  # date, e.g. 1988-10-27
427db96d56Sopenharmony_ci(?:
437db96d56Sopenharmony_ci    [Tt ]
447db96d56Sopenharmony_ci    {_TIME_RE_STR}
457db96d56Sopenharmony_ci    (?:([Zz])|([+-])([01][0-9]|2[0-3]):([0-5][0-9]))?  # optional time offset
467db96d56Sopenharmony_ci)?
477db96d56Sopenharmony_ci""",
487db96d56Sopenharmony_ci    flags=re.VERBOSE,
497db96d56Sopenharmony_ci)
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_cidef match_to_datetime(match: re.Match) -> datetime | date:
537db96d56Sopenharmony_ci    """Convert a `RE_DATETIME` match to `datetime.datetime` or `datetime.date`.
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ci    Raises ValueError if the match does not correspond to a valid date
567db96d56Sopenharmony_ci    or datetime.
577db96d56Sopenharmony_ci    """
587db96d56Sopenharmony_ci    (
597db96d56Sopenharmony_ci        year_str,
607db96d56Sopenharmony_ci        month_str,
617db96d56Sopenharmony_ci        day_str,
627db96d56Sopenharmony_ci        hour_str,
637db96d56Sopenharmony_ci        minute_str,
647db96d56Sopenharmony_ci        sec_str,
657db96d56Sopenharmony_ci        micros_str,
667db96d56Sopenharmony_ci        zulu_time,
677db96d56Sopenharmony_ci        offset_sign_str,
687db96d56Sopenharmony_ci        offset_hour_str,
697db96d56Sopenharmony_ci        offset_minute_str,
707db96d56Sopenharmony_ci    ) = match.groups()
717db96d56Sopenharmony_ci    year, month, day = int(year_str), int(month_str), int(day_str)
727db96d56Sopenharmony_ci    if hour_str is None:
737db96d56Sopenharmony_ci        return date(year, month, day)
747db96d56Sopenharmony_ci    hour, minute, sec = int(hour_str), int(minute_str), int(sec_str)
757db96d56Sopenharmony_ci    micros = int(micros_str.ljust(6, "0")) if micros_str else 0
767db96d56Sopenharmony_ci    if offset_sign_str:
777db96d56Sopenharmony_ci        tz: tzinfo | None = cached_tz(
787db96d56Sopenharmony_ci            offset_hour_str, offset_minute_str, offset_sign_str
797db96d56Sopenharmony_ci        )
807db96d56Sopenharmony_ci    elif zulu_time:
817db96d56Sopenharmony_ci        tz = timezone.utc
827db96d56Sopenharmony_ci    else:  # local date-time
837db96d56Sopenharmony_ci        tz = None
847db96d56Sopenharmony_ci    return datetime(year, month, day, hour, minute, sec, micros, tzinfo=tz)
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_ci
877db96d56Sopenharmony_ci@lru_cache(maxsize=None)
887db96d56Sopenharmony_cidef cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone:
897db96d56Sopenharmony_ci    sign = 1 if sign_str == "+" else -1
907db96d56Sopenharmony_ci    return timezone(
917db96d56Sopenharmony_ci        timedelta(
927db96d56Sopenharmony_ci            hours=sign * int(hour_str),
937db96d56Sopenharmony_ci            minutes=sign * int(minute_str),
947db96d56Sopenharmony_ci        )
957db96d56Sopenharmony_ci    )
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_cidef match_to_localtime(match: re.Match) -> time:
997db96d56Sopenharmony_ci    hour_str, minute_str, sec_str, micros_str = match.groups()
1007db96d56Sopenharmony_ci    micros = int(micros_str.ljust(6, "0")) if micros_str else 0
1017db96d56Sopenharmony_ci    return time(int(hour_str), int(minute_str), int(sec_str), micros)
1027db96d56Sopenharmony_ci
1037db96d56Sopenharmony_ci
1047db96d56Sopenharmony_cidef match_to_number(match: re.Match, parse_float: ParseFloat) -> Any:
1057db96d56Sopenharmony_ci    if match.group("floatpart"):
1067db96d56Sopenharmony_ci        return parse_float(match.group())
1077db96d56Sopenharmony_ci    return int(match.group(), 0)
108