Spaces:
Runtime error
Runtime error
| """Provides the :class:`Arrow <arrow.formatter.DateTimeFormatter>` class, an improved formatter for datetimes.""" | |
| import re | |
| import sys | |
| from datetime import datetime, timedelta | |
| from typing import Optional, Pattern, cast | |
| from dateutil import tz as dateutil_tz | |
| from arrow import locales | |
| from arrow.constants import DEFAULT_LOCALE | |
| if sys.version_info < (3, 8): # pragma: no cover | |
| from typing_extensions import Final | |
| else: | |
| from typing import Final # pragma: no cover | |
| FORMAT_ATOM: Final[str] = "YYYY-MM-DD HH:mm:ssZZ" | |
| FORMAT_COOKIE: Final[str] = "dddd, DD-MMM-YYYY HH:mm:ss ZZZ" | |
| FORMAT_RFC822: Final[str] = "ddd, DD MMM YY HH:mm:ss Z" | |
| FORMAT_RFC850: Final[str] = "dddd, DD-MMM-YY HH:mm:ss ZZZ" | |
| FORMAT_RFC1036: Final[str] = "ddd, DD MMM YY HH:mm:ss Z" | |
| FORMAT_RFC1123: Final[str] = "ddd, DD MMM YYYY HH:mm:ss Z" | |
| FORMAT_RFC2822: Final[str] = "ddd, DD MMM YYYY HH:mm:ss Z" | |
| FORMAT_RFC3339: Final[str] = "YYYY-MM-DD HH:mm:ssZZ" | |
| FORMAT_RSS: Final[str] = "ddd, DD MMM YYYY HH:mm:ss Z" | |
| FORMAT_W3C: Final[str] = "YYYY-MM-DD HH:mm:ssZZ" | |
| class DateTimeFormatter: | |
| # This pattern matches characters enclosed in square brackets are matched as | |
| # an atomic group. For more info on atomic groups and how to they are | |
| # emulated in Python's re library, see https://stackoverflow.com/a/13577411/2701578 | |
| _FORMAT_RE: Final[Pattern[str]] = re.compile( | |
| r"(\[(?:(?=(?P<literal>[^]]))(?P=literal))*\]|YYY?Y?|MM?M?M?|Do|DD?D?D?|d?dd?d?|HH?|hh?|mm?|ss?|SS?S?S?S?S?|ZZ?Z?|a|A|X|x|W)" | |
| ) | |
| locale: locales.Locale | |
| def __init__(self, locale: str = DEFAULT_LOCALE) -> None: | |
| self.locale = locales.get_locale(locale) | |
| def format(cls, dt: datetime, fmt: str) -> str: | |
| # FIXME: _format_token() is nullable | |
| return cls._FORMAT_RE.sub( | |
| lambda m: cast(str, cls._format_token(dt, m.group(0))), fmt | |
| ) | |
| def _format_token(self, dt: datetime, token: Optional[str]) -> Optional[str]: | |
| if token and token.startswith("[") and token.endswith("]"): | |
| return token[1:-1] | |
| if token == "YYYY": | |
| return self.locale.year_full(dt.year) | |
| if token == "YY": | |
| return self.locale.year_abbreviation(dt.year) | |
| if token == "MMMM": | |
| return self.locale.month_name(dt.month) | |
| if token == "MMM": | |
| return self.locale.month_abbreviation(dt.month) | |
| if token == "MM": | |
| return f"{dt.month:02d}" | |
| if token == "M": | |
| return f"{dt.month}" | |
| if token == "DDDD": | |
| return f"{dt.timetuple().tm_yday:03d}" | |
| if token == "DDD": | |
| return f"{dt.timetuple().tm_yday}" | |
| if token == "DD": | |
| return f"{dt.day:02d}" | |
| if token == "D": | |
| return f"{dt.day}" | |
| if token == "Do": | |
| return self.locale.ordinal_number(dt.day) | |
| if token == "dddd": | |
| return self.locale.day_name(dt.isoweekday()) | |
| if token == "ddd": | |
| return self.locale.day_abbreviation(dt.isoweekday()) | |
| if token == "d": | |
| return f"{dt.isoweekday()}" | |
| if token == "HH": | |
| return f"{dt.hour:02d}" | |
| if token == "H": | |
| return f"{dt.hour}" | |
| if token == "hh": | |
| return f"{dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12):02d}" | |
| if token == "h": | |
| return f"{dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12)}" | |
| if token == "mm": | |
| return f"{dt.minute:02d}" | |
| if token == "m": | |
| return f"{dt.minute}" | |
| if token == "ss": | |
| return f"{dt.second:02d}" | |
| if token == "s": | |
| return f"{dt.second}" | |
| if token == "SSSSSS": | |
| return f"{dt.microsecond:06d}" | |
| if token == "SSSSS": | |
| return f"{dt.microsecond // 10:05d}" | |
| if token == "SSSS": | |
| return f"{dt.microsecond // 100:04d}" | |
| if token == "SSS": | |
| return f"{dt.microsecond // 1000:03d}" | |
| if token == "SS": | |
| return f"{dt.microsecond // 10000:02d}" | |
| if token == "S": | |
| return f"{dt.microsecond // 100000}" | |
| if token == "X": | |
| return f"{dt.timestamp()}" | |
| if token == "x": | |
| return f"{dt.timestamp() * 1_000_000:.0f}" | |
| if token == "ZZZ": | |
| return dt.tzname() | |
| if token in ["ZZ", "Z"]: | |
| separator = ":" if token == "ZZ" else "" | |
| tz = dateutil_tz.tzutc() if dt.tzinfo is None else dt.tzinfo | |
| # `dt` must be aware object. Otherwise, this line will raise AttributeError | |
| # https://github.com/arrow-py/arrow/pull/883#discussion_r529866834 | |
| # datetime awareness: https://docs.python.org/3/library/datetime.html#aware-and-naive-objects | |
| total_minutes = int(cast(timedelta, tz.utcoffset(dt)).total_seconds() / 60) | |
| sign = "+" if total_minutes >= 0 else "-" | |
| total_minutes = abs(total_minutes) | |
| hour, minute = divmod(total_minutes, 60) | |
| return f"{sign}{hour:02d}{separator}{minute:02d}" | |
| if token in ("a", "A"): | |
| return self.locale.meridian(dt.hour, token) | |
| if token == "W": | |
| year, week, day = dt.isocalendar() | |
| return f"{year}-W{week:02d}-{day}" | |