Update arrow-1.2.0

This commit is contained in:
JonnyWong16 2021-10-14 20:43:42 -07:00
parent 3b645cf6c3
commit 9a54fb9a44
No known key found for this signature in database
GPG key ID: B1F1F9807184697A
11 changed files with 7793 additions and 2322 deletions

View file

@ -1,105 +1,152 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
"""Provides the :class:`Arrow <arrow.formatter.DateTimeFormatter>` class, an improved formatter for datetimes."""
import calendar
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 util, locales
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
class DateTimeFormatter(object):
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"
_FORMAT_RE = re.compile('(YYY?Y?|MM?M?M?|Do|DD?D?D?|d?dd?d?|HH?|hh?|mm?|ss?|SS?S?S?S?S?|ZZ?|a|A|X)')
def __init__(self, locale='en_us'):
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, fmt):
def format(cls, dt: datetime, fmt: str) -> str:
return cls._FORMAT_RE.sub(lambda m: cls._format_token(dt, m.group(0)), fmt)
# 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, token):
def _format_token(self, dt: datetime, token: Optional[str]) -> Optional[str]:
if token == 'YYYY':
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':
if token == "YY":
return self.locale.year_abbreviation(dt.year)
if token == 'MMMM':
if token == "MMMM":
return self.locale.month_name(dt.month)
if token == 'MMM':
if token == "MMM":
return self.locale.month_abbreviation(dt.month)
if token == 'MM':
return '{0:02d}'.format(dt.month)
if token == 'M':
return str(dt.month)
if token == "MM":
return f"{dt.month:02d}"
if token == "M":
return f"{dt.month}"
if token == 'DDDD':
return '{0:03d}'.format(dt.timetuple().tm_yday)
if token == 'DDD':
return str(dt.timetuple().tm_yday)
if token == 'DD':
return '{0:02d}'.format(dt.day)
if token == 'D':
return str(dt.day)
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':
if token == "Do":
return self.locale.ordinal_number(dt.day)
if token == 'dddd':
if token == "dddd":
return self.locale.day_name(dt.isoweekday())
if token == 'ddd':
if token == "ddd":
return self.locale.day_abbreviation(dt.isoweekday())
if token == 'd':
return str(dt.isoweekday())
if token == "d":
return f"{dt.isoweekday()}"
if token == 'HH':
return '{0:02d}'.format(dt.hour)
if token == 'H':
return str(dt.hour)
if token == 'hh':
return '{0:02d}'.format(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
if token == 'h':
return str(dt.hour if 0 < dt.hour < 13 else abs(dt.hour - 12))
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 '{0:02d}'.format(dt.minute)
if token == 'm':
return str(dt.minute)
if token == "mm":
return f"{dt.minute:02d}"
if token == "m":
return f"{dt.minute}"
if token == 'ss':
return '{0:02d}'.format(dt.second)
if token == 's':
return str(dt.second)
if token == "ss":
return f"{dt.second:02d}"
if token == "s":
return f"{dt.second}"
if token == 'SSSSSS':
return str('{0:06d}'.format(int(dt.microsecond)))
if token == 'SSSSS':
return str('{0:05d}'.format(int(dt.microsecond / 10)))
if token == 'SSSS':
return str('{0:04d}'.format(int(dt.microsecond / 100)))
if token == 'SSS':
return str('{0:03d}'.format(int(dt.microsecond / 1000)))
if token == 'SS':
return str('{0:02d}'.format(int(dt.microsecond / 10000)))
if token == 'S':
return str(int(dt.microsecond / 100000))
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 str(calendar.timegm(dt.utctimetuple()))
if token == "X":
return f"{dt.timestamp()}"
if token in ['ZZ', 'Z']:
separator = ':' if token == 'ZZ' else ''
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
total_minutes = int(util.total_seconds(tz.utcoffset(dt)) / 60)
# `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 '-'
sign = "+" if total_minutes >= 0 else "-"
total_minutes = abs(total_minutes)
hour, minute = divmod(total_minutes, 60)
return '{0}{1:02d}{2}{3:02d}'.format(sign, hour, separator, minute)
return f"{sign}{hour:02d}{separator}{minute:02d}"
if token in ('a', 'A'):
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}"