spdxlicenseidentifier mit spdxfiletext 2021 taneli hukkinen licensed to psf under a contributor agreement e g 00 32 00 999999 00 32 00 flagsre verbose relocaltime re compiletimerestr redatetime re compile rf 0940191020191209301 date e g 19881027 tt timerestr zz0109203 0509 optional time offset convert a redatetime match to datetime datetime or datetime date raises valueerror if the match does not correspond to a valid date or datetime spdx license identifier mit spdx filetext 2021 taneli hukkinen licensed to psf under a contributor agreement e g 00 32 00 999999 00 32 00 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 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 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 local date time
from __future__ import annotations

from datetime import date, datetime, time, timedelta, timezone, tzinfo
from functools import lru_cache
import re
from typing import Any

from ._types import ParseFloat




_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,
    flags=re.VERBOSE,
)
RE_LOCALTIME = re.compile(_TIME_RE_STR)
RE_DATETIME = re.compile(
    rf,
    flags=re.VERBOSE,
)


def match_to_datetime(match: re.Match) -> datetime | date:
    
    (
        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: tzinfo | None = cached_tz(
            offset_hour_str, offset_minute_str, offset_sign_str
        )
    elif zulu_time:
        tz = timezone.utc
    else:  
        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)