Source code for dateparser.calendars

from collections.abc import Callable
from datetime import datetime, tzinfo
from typing import Any, ClassVar, Protocol

from dateparser.conf import Settings, settings
from dateparser.date import DateData
from dateparser.parser import _parser


class _CalendarParser(Protocol):
    def _parse_with_parts(
        self, datestring: str, settings: Settings
    ) -> tuple[datetime, str | None, tuple[str, ...]]: ...


class _PartlessCalendarParser(Protocol):
    def parse(
        self, datestring: str, settings: Settings
    ) -> tuple[datetime, str | None]: ...


[docs] class CalendarBase: """Base setup class for non-Gregorian calendar system. :param source: Date string passed to calendar parser. :type source: str """ parser: _CalendarParser | _PartlessCalendarParser = NotImplemented def __init__(self, source: str) -> None: self.source = source def get_date(self) -> DateData | None: try: if hasattr(self.parser, "_parse_with_parts"): date_obj, period, parts = self.parser._parse_with_parts( self.source, settings ) else: date_obj, period = self.parser.parse(self.source, settings) parts = () return DateData(date_obj=date_obj, period=period, parts=parts) except ValueError: return None
class non_gregorian_parser(_parser): calendar_converter: Any = NotImplemented default_year: int = NotImplemented default_month: int = NotImplemented default_day: int = NotImplemented non_gregorian_date_cls: Callable[[int, int, int], Any] = NotImplemented _digits: ClassVar[dict[str, int] | None] = None _months: ClassVar[dict[str, tuple[Any, ...]] | None] = None _weekdays: ClassVar[dict[str, list[str]] | None] = None _number_letters: ClassVar[dict[int, list[str]] | None] = None @classmethod def _replace_time_conventions(cls, source: str) -> str: return source @classmethod def _replace_digits(cls, source: str) -> str: return source @classmethod def _replace_months(cls, source: str) -> str: return source @classmethod def _replace_weekdays(cls, source: str) -> str: return source @classmethod def _replace_time(cls, source: str) -> str: return source @classmethod def _replace_days(cls, source: str) -> str: return source @classmethod def to_latin(cls, source: str) -> str: result = source result = cls._replace_months(result) result = cls._replace_weekdays(result) result = cls._replace_digits(result) result = cls._replace_days(result) result = cls._replace_time(result) result = cls._replace_time_conventions(result) return result.strip() def handle_two_digit_year(self, year: int) -> int: raise ValueError def _get_datetime_obj(self, **params: Any) -> datetime: day = params["day"] year = params["year"] month = params["month"] if not (0 < day <= self.calendar_converter.month_length(year, month)) and not ( self._token_day or hasattr(self, "_token_weekday") ): day = self.calendar_converter.month_length(year, month) year, month, day = self.calendar_converter.to_gregorian( year=year, month=month, day=day ) c_params = params.copy() c_params.update({"year": year, "month": month, "day": day}) return datetime(**c_params) def _get_datetime_obj_params(self) -> dict[str, int]: if not self.now: self._set_relative_base() now_year, now_month, now_day = self.calendar_converter.from_gregorian( self.now.year, self.now.month, self.now.day ) return { "day": self.day or now_day, "month": self.month or now_month, "year": self.year or now_year, "hour": 0, "minute": 0, "second": 0, "microsecond": 0, } def _get_date_obj(self, token: str, directive: str) -> Any: year, month, day = self.default_year, self.default_month, self.default_day token_len = len(token) is_digit = token.isdigit() if directive == "%A" and self._weekdays and token.title() in self._weekdays: pass elif ( directive == "%m" and token_len <= 2 and is_digit and 1 <= int(token) <= 12 ): month = int(token) elif directive == "%B" and self._months and token in self._months: month = self._months[token][0] elif ( directive == "%d" and token_len <= 2 and is_digit and 0 < int(token) <= self.calendar_converter.month_length(year, month) ): day = int(token) elif directive == "%Y" and token_len == 4 and is_digit: year = int(token) elif directive == "%Y" and token_len == 2 and is_digit: year = self.handle_two_digit_year(int(token)) else: raise ValueError return self.non_gregorian_date_cls(year, month, day) @classmethod def parse( cls, datestring: str, settings: Settings, tz: tzinfo | None = None, date_order: str | None = None, ) -> tuple[datetime, str | None]: date_obj, period, _ = cls._parse_with_parts( datestring, settings, tz, date_order ) return date_obj, period @classmethod def _parse_with_parts( cls, datestring: str, settings: Settings, tz: tzinfo | None = None, date_order: str | None = None, ) -> tuple[datetime, str | None, tuple[str, ...]]: datestring = cls.to_latin(datestring) return super()._parse_with_parts(datestring, settings, tz, date_order)