From 17f63486e000bcb7aa5f789f2912e5fb25894e3d Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 11 Jan 2023 13:20:09 +0200 Subject: [PATCH 1/2] Unify `import datetime` --- babel/dates.py | 247 +++++++++++++++++++++++------------- babel/localtime/__init__.py | 20 +-- babel/localtime/_unix.py | 6 +- babel/localtime/_win32.py | 4 +- babel/messages/catalog.py | 21 +-- babel/messages/frontend.py | 6 +- babel/numbers.py | 24 ++-- babel/support.py | 33 +++-- babel/util.py | 12 +- 9 files changed, 223 insertions(+), 150 deletions(-) diff --git a/babel/dates.py b/babel/dates.py index f7289619f..05cfa251c 100644 --- a/babel/dates.py +++ b/babel/dates.py @@ -29,7 +29,7 @@ from bisect import bisect_right from collections.abc import Iterable -from datetime import date, datetime, time, timedelta, tzinfo +import datetime from babel import localtime from babel.core import Locale, default_locale, get_global @@ -37,10 +37,10 @@ if TYPE_CHECKING: from typing_extensions import Literal, TypeAlias - _Instant: TypeAlias = date | time | float | None + _Instant: TypeAlias = datetime.date | datetime.time | float | None _PredefinedTimeFormat: TypeAlias = Literal['full', 'long', 'medium', 'short'] _Context: TypeAlias = Literal['format', 'stand-alone'] - _DtOrTzinfo: TypeAlias = datetime | tzinfo | str | int | time | None + _DtOrTzinfo: TypeAlias = datetime.datetime | datetime.tzinfo | str | int | datetime.time | None # "If a given short metazone form is known NOT to be understood in a given # locale and the parent locale has this value such that it would normally @@ -60,13 +60,8 @@ LC_TIME = default_locale('LC_TIME') -# Aliases for use in scopes where the modules are shadowed by local variables -date_ = date -datetime_ = datetime -time_ = time - -def _localize(tz: tzinfo, dt: datetime) -> datetime: +def _localize(tz: datetime.tzinfo, dt: datetime.datetime) -> datetime.datetime: # Support localizing with both pytz and zoneinfo tzinfos # nothing to do if dt.tzinfo is tz: @@ -83,8 +78,7 @@ def _localize(tz: tzinfo, dt: datetime) -> datetime: return dt.astimezone(tz) - -def _get_dt_and_tzinfo(dt_or_tzinfo: _DtOrTzinfo) -> tuple[datetime_ | None, tzinfo]: +def _get_dt_and_tzinfo(dt_or_tzinfo: _DtOrTzinfo) -> tuple[datetime.datetime | None, datetime.tzinfo]: """ Parse a `dt_or_tzinfo` value into a datetime and a tzinfo. @@ -93,7 +87,7 @@ def _get_dt_and_tzinfo(dt_or_tzinfo: _DtOrTzinfo) -> tuple[datetime_ | None, tzi :rtype: tuple[datetime, tzinfo] """ if dt_or_tzinfo is None: - dt = datetime.now() + dt = datetime.datetime.now() tzinfo = LOCALTZ elif isinstance(dt_or_tzinfo, str): dt = None @@ -101,7 +95,7 @@ def _get_dt_and_tzinfo(dt_or_tzinfo: _DtOrTzinfo) -> tuple[datetime_ | None, tzi elif isinstance(dt_or_tzinfo, int): dt = None tzinfo = UTC - elif isinstance(dt_or_tzinfo, (datetime, time)): + elif isinstance(dt_or_tzinfo, (datetime.datetime, datetime.time)): dt = _get_datetime(dt_or_tzinfo) if dt.tzinfo is not None: tzinfo = dt.tzinfo @@ -125,10 +119,10 @@ def _get_tz_name(dt_or_tzinfo: _DtOrTzinfo) -> str: elif hasattr(tzinfo, 'key') and tzinfo.key is not None: # ZoneInfo object return tzinfo.key else: - return tzinfo.tzname(dt or datetime.utcnow()) + return tzinfo.tzname(dt or datetime.datetime.utcnow()) -def _get_datetime(instant: _Instant) -> datetime_: +def _get_datetime(instant: _Instant) -> datetime.datetime: """ Get a datetime out of an "instant" (date, time, datetime, number). @@ -139,6 +133,7 @@ def _get_datetime(instant: _Instant) -> datetime_: Dates are converted to naive datetimes with midnight as the time component. + >>> from datetime import date, datetime >>> _get_datetime(date(2015, 1, 1)) datetime.datetime(2015, 1, 1, 0, 0) @@ -159,18 +154,18 @@ def _get_datetime(instant: _Instant) -> datetime_: :rtype: datetime """ if instant is None: - return datetime_.utcnow() + return datetime.datetime.utcnow() elif isinstance(instant, int) or isinstance(instant, float): - return datetime_.utcfromtimestamp(instant) - elif isinstance(instant, time): - return datetime_.combine(date.today(), instant) - elif isinstance(instant, date) and not isinstance(instant, datetime): - return datetime_.combine(instant, time()) + return datetime.datetime.utcfromtimestamp(instant) + elif isinstance(instant, datetime.time): + return datetime.datetime.combine(datetime.date.today(), instant) + elif isinstance(instant, datetime.date) and not isinstance(instant, datetime.datetime): + return datetime.datetime.combine(instant, datetime.time()) # TODO (3.x): Add an assertion/type check for this fallthrough branch: return instant -def _ensure_datetime_tzinfo(datetime: datetime_, tzinfo: tzinfo | None = None) -> datetime_: +def _ensure_datetime_tzinfo(dt: datetime.datetime, tzinfo: datetime.tzinfo | None = None) -> datetime.datetime: """ Ensure the datetime passed has an attached tzinfo. @@ -178,6 +173,7 @@ def _ensure_datetime_tzinfo(datetime: datetime_, tzinfo: tzinfo | None = None) - If a tzinfo is passed in, the datetime is normalized to that timezone. + >>> from datetime import datetime >>> _get_tz_name(_ensure_datetime_tzinfo(datetime(2015, 1, 1))) 'UTC' @@ -190,16 +186,19 @@ def _ensure_datetime_tzinfo(datetime: datetime_, tzinfo: tzinfo | None = None) - :return: datetime with tzinfo :rtype: datetime """ - if datetime.tzinfo is None: - datetime = datetime.replace(tzinfo=UTC) + if dt.tzinfo is None: + dt = dt.replace(tzinfo=UTC) if tzinfo is not None: - datetime = datetime.astimezone(get_timezone(tzinfo)) + dt = dt.astimezone(get_timezone(tzinfo)) if hasattr(tzinfo, 'normalize'): # pytz - datetime = tzinfo.normalize(datetime) - return datetime + dt = tzinfo.normalize(dt) + return dt -def _get_time(time: time | datetime | None, tzinfo: tzinfo | None = None) -> time: +def _get_time( + time: datetime.time | datetime.datetime | None, + tzinfo: datetime.tzinfo | None = None, +) -> datetime.time: """ Get a timezoned time from a given instant. @@ -209,14 +208,14 @@ def _get_time(time: time | datetime | None, tzinfo: tzinfo | None = None) -> tim :rtype: time """ if time is None: - time = datetime.utcnow() + time = datetime.datetime.utcnow() elif isinstance(time, (int, float)): - time = datetime.utcfromtimestamp(time) + time = datetime.datetime.utcfromtimestamp(time) if time.tzinfo is None: time = time.replace(tzinfo=UTC) - if isinstance(time, datetime): + if isinstance(time, datetime.datetime): if tzinfo is not None: time = time.astimezone(tzinfo) if hasattr(tzinfo, 'normalize'): # pytz @@ -227,7 +226,7 @@ def _get_time(time: time | datetime | None, tzinfo: tzinfo | None = None) -> tim return time -def get_timezone(zone: str | tzinfo | None = None) -> tzinfo: +def get_timezone(zone: str | datetime.tzinfo | None = None) -> datetime.tzinfo: """Looks up a timezone by name and returns it. The timezone object returned comes from ``pytz`` or ``zoneinfo``, whichever is available. It corresponds to the `tzinfo` interface and can be used with all of @@ -260,7 +259,7 @@ def get_timezone(zone: str | tzinfo | None = None) -> tzinfo: raise LookupError(f"Unknown timezone {zone}") from exc -def get_next_timezone_transition(zone: tzinfo | None = None, dt: _Instant = None) -> TimezoneTransition: +def get_next_timezone_transition(zone: datetime.tzinfo | None = None, dt: _Instant = None) -> TimezoneTransition: """Given a timezone it will return a :class:`TimezoneTransition` object that holds the information about the next timezone transition that's going to happen. For instance this can be used to detect when the next DST @@ -332,7 +331,13 @@ class TimezoneTransition: to the :func:`get_next_timezone_transition`. """ - def __init__(self, activates: datetime_, from_tzinfo: tzinfo, to_tzinfo: tzinfo, reference_date: datetime_ | None = None): + def __init__( + self, + activates: datetime.datetime, + from_tzinfo: datetime.tzinfo, + to_tzinfo: datetime.tzinfo, + reference_date: datetime.datetime | None = None, + ) -> None: warnings.warn( "TimezoneTransition is deprecated and will be " "removed in the next version of Babel. " @@ -503,11 +508,16 @@ def get_time_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | s return Locale.parse(locale).time_formats[format] -def get_timezone_gmt(datetime: _Instant = None, width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long', - locale: Locale | str | None = LC_TIME, return_z: bool = False) -> str: +def get_timezone_gmt( + datetime: _Instant = None, + width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long', + locale: Locale | str | None = LC_TIME, + return_z: bool = False, +) -> str: """Return the timezone associated with the given `datetime` object formatted as string indicating the offset from GMT. + >>> from datetime import datetime >>> dt = datetime(2007, 4, 1, 15, 30) >>> get_timezone_gmt(dt, locale='en') u'GMT+00:00' @@ -558,9 +568,12 @@ def get_timezone_gmt(datetime: _Instant = None, width: Literal['long', 'short', return pattern % (hours, seconds // 60) -def get_timezone_location(dt_or_tzinfo: _DtOrTzinfo = None, locale: Locale | str | None = LC_TIME, - return_city: bool = False) -> str: - u"""Return a representation of the given timezone using "location format". +def get_timezone_location( + dt_or_tzinfo: _DtOrTzinfo = None, + locale: Locale | str | None = LC_TIME, + return_city: bool = False, +) -> str: + """Return a representation of the given timezone using "location format". The result depends on both the local display name of the country and the city associated with the time zone: @@ -635,12 +648,18 @@ def get_timezone_location(dt_or_tzinfo: _DtOrTzinfo = None, locale: Locale | str }) -def get_timezone_name(dt_or_tzinfo: _DtOrTzinfo = None, width: Literal['long', 'short'] = 'long', uncommon: bool = False, - locale: Locale | str | None = LC_TIME, zone_variant: Literal['generic', 'daylight', 'standard'] | None = None, - return_zone: bool = False) -> str: +def get_timezone_name( + dt_or_tzinfo: _DtOrTzinfo = None, + width: Literal['long', 'short'] = 'long', + uncommon: bool = False, + locale: Locale | str | None = LC_TIME, + zone_variant: Literal['generic', 'daylight', 'standard'] | None = None, + return_zone: bool = False, +) -> str: r"""Return the localized display name for the given timezone. The timezone may be specified using a ``datetime`` or `tzinfo` object. + >>> from datetime import time >>> dt = time(15, 30, tzinfo=get_timezone('America/Los_Angeles')) >>> get_timezone_name(dt, locale='en_US') u'Pacific Standard Time' @@ -755,10 +774,14 @@ def get_timezone_name(dt_or_tzinfo: _DtOrTzinfo = None, width: Literal['long', ' return get_timezone_location(dt_or_tzinfo, locale=locale) -def format_date(date: date | None = None, format: _PredefinedTimeFormat | str = 'medium', - locale: Locale | str | None = LC_TIME) -> str: +def format_date( + date: datetime.date | None = None, + format: _PredefinedTimeFormat | str = 'medium', + locale: Locale | str | None = LC_TIME, +) -> str: """Return a date formatted according to the given pattern. + >>> from datetime import date >>> d = date(2007, 4, 1) >>> format_date(d, locale='en_US') u'Apr 1, 2007' @@ -778,8 +801,8 @@ def format_date(date: date | None = None, format: _PredefinedTimeFormat | str = :param locale: a `Locale` object or a locale identifier """ if date is None: - date = date_.today() - elif isinstance(date, datetime): + date = datetime.date.today() + elif isinstance(date, datetime.datetime): date = date.date() locale = Locale.parse(locale) @@ -789,10 +812,15 @@ def format_date(date: date | None = None, format: _PredefinedTimeFormat | str = return pattern.apply(date, locale) -def format_datetime(datetime: _Instant = None, format: _PredefinedTimeFormat | str = 'medium', tzinfo: tzinfo | None = None, - locale: Locale | str | None = LC_TIME) -> str: +def format_datetime( + datetime: _Instant = None, + format: _PredefinedTimeFormat | str = 'medium', + tzinfo: datetime.tzinfo | None = None, + locale: Locale | str | None = LC_TIME, +) -> str: r"""Return a date formatted according to the given pattern. + >>> from datetime import datetime >>> dt = datetime(2007, 4, 1, 15, 30) >>> format_datetime(dt, locale='en_US') u'Apr 1, 2007, 3:30:00 PM' @@ -826,10 +854,14 @@ def format_datetime(datetime: _Instant = None, format: _PredefinedTimeFormat | s return parse_pattern(format).apply(datetime, locale) -def format_time(time: time | datetime | float | None = None, format: _PredefinedTimeFormat | str = 'medium', - tzinfo: tzinfo | None = None, locale: Locale | str | None = LC_TIME) -> str: +def format_time( + time: datetime.time | datetime.datetime | float | None = None, + format: _PredefinedTimeFormat | str = 'medium', + tzinfo: datetime.tzinfo | None = None, locale: Locale | str | None = LC_TIME, +) -> str: r"""Return a time formatted according to the given pattern. + >>> from datetime import datetime, time >>> t = time(15, 30) >>> format_time(t, locale='en_US') u'3:30:00 PM' @@ -885,7 +917,7 @@ def format_time(time: time | datetime | float | None = None, format: _Predefined # get reference date for if we need to find the right timezone variant # in the pattern - ref_date = time.date() if isinstance(time, datetime) else None + ref_date = time.date() if isinstance(time, datetime.datetime) else None time = _get_time(time, tzinfo) @@ -895,8 +927,13 @@ def format_time(time: time | datetime | float | None = None, format: _Predefined return parse_pattern(format).apply(time, locale, reference_date=ref_date) -def format_skeleton(skeleton: str, datetime: _Instant = None, tzinfo: tzinfo | None = None, - fuzzy: bool = True, locale: Locale | str | None = LC_TIME) -> str: +def format_skeleton( + skeleton: str, + datetime: _Instant = None, + tzinfo: datetime.tzinfo | None = None, + fuzzy: bool = True, + locale: Locale | str | None = LC_TIME, +) -> str: r"""Return a time and/or date formatted according to the given pattern. The skeletons are defined in the CLDR data and provide more flexibility @@ -904,6 +941,7 @@ def format_skeleton(skeleton: str, datetime: _Instant = None, tzinfo: tzinfo | N The are defined using the date/time symbols without order or punctuation and map to a suitable format for the given locale. + >>> from datetime import datetime >>> t = datetime(2007, 4, 1, 15, 30) >>> format_skeleton('MMMEd', t, locale='fr') u'dim. 1 avr.' @@ -945,12 +983,17 @@ def format_skeleton(skeleton: str, datetime: _Instant = None, tzinfo: tzinfo | N ) -def format_timedelta(delta: timedelta | int, - granularity: Literal['year', 'month', 'week', 'day', 'hour', 'minute', 'second'] = 'second', - threshold: float = .85, add_direction: bool = False, format: Literal['narrow', 'short', 'medium', 'long'] = 'long', - locale: Locale | str | None = LC_TIME) -> str: +def format_timedelta( + delta: datetime.timedelta | int, + granularity: Literal['year', 'month', 'week', 'day', 'hour', 'minute', 'second'] = 'second', + threshold: float = .85, + add_direction: bool = False, + format: Literal['narrow', 'short', 'medium', 'long'] = 'long', + locale: Locale | str | None = LC_TIME, +) -> str: """Return a time delta according to the rules of the given locale. + >>> from datetime import timedelta >>> format_timedelta(timedelta(weeks=12), locale='en_US') u'3 months' >>> format_timedelta(timedelta(seconds=1), locale='es') @@ -959,8 +1002,7 @@ def format_timedelta(delta: timedelta | int, The granularity parameter can be provided to alter the lowest unit presented, which defaults to a second. - >>> format_timedelta(timedelta(hours=3), granularity='day', - ... locale='en_US') + >>> format_timedelta(timedelta(hours=3), granularity='day', locale='en_US') u'1 day' The threshold parameter can be used to determine at which value the @@ -1011,7 +1053,7 @@ def format_timedelta(delta: timedelta | int, ' is deprecated. Use "long" instead', category=DeprecationWarning) format = 'long' - if isinstance(delta, timedelta): + if isinstance(delta, datetime.timedelta): seconds = int((delta.days * 86400) + delta.seconds) else: seconds = delta @@ -1047,13 +1089,18 @@ def _iter_patterns(a_unit): return u'' -def _format_fallback_interval(start: _Instant, end: _Instant, skeleton: str | None, tzinfo: tzinfo | None, - locale: Locale | str | None = LC_TIME) -> str: +def _format_fallback_interval( + start: _Instant, + end: _Instant, + skeleton: str | None, + tzinfo: datetime.tzinfo | None, + locale: Locale | str | None = LC_TIME, +) -> str: if skeleton in locale.datetime_skeletons: # Use the given skeleton format = lambda dt: format_skeleton(skeleton, dt, tzinfo, locale=locale) - elif all((isinstance(d, date) and not isinstance(d, datetime)) for d in (start, end)): # Both are just dates + elif all((isinstance(d, datetime.date) and not isinstance(d, datetime.datetime)) for d in (start, end)): # Both are just dates format = lambda dt: format_date(dt, locale=locale) - elif all((isinstance(d, time) and not isinstance(d, date)) for d in (start, end)): # Both are times + elif all((isinstance(d, datetime.time) and not isinstance(d, datetime.date)) for d in (start, end)): # Both are times format = lambda dt: format_time(dt, tzinfo=tzinfo, locale=locale) else: format = lambda dt: format_datetime(dt, tzinfo=tzinfo, locale=locale) @@ -1071,11 +1118,18 @@ def _format_fallback_interval(start: _Instant, end: _Instant, skeleton: str | No ) -def format_interval(start: _Instant, end: _Instant, skeleton: str | None = None, tzinfo: tzinfo | None = None, - fuzzy: bool = True, locale: Locale | str | None = LC_TIME) -> str: +def format_interval( + start: _Instant, + end: _Instant, + skeleton: str | None = None, + tzinfo: datetime.tzinfo | None = None, + fuzzy: bool = True, + locale: Locale | str | None = LC_TIME, +) -> str: """ Format an interval between two instants according to the locale's rules. + >>> from datetime import date, time >>> format_interval(date(2016, 1, 15), date(2016, 1, 17), "yMd", locale="fi") u'15.\u201317.1.2016' @@ -1170,13 +1224,18 @@ def format_interval(start: _Instant, end: _Instant, skeleton: str | None = None, return _format_fallback_interval(start, end, skeleton, tzinfo, locale) -def get_period_id(time: _Instant, tzinfo: tzinfo | None = None, type: Literal['selection'] | None = None, - locale: Locale | str | None = LC_TIME) -> str: +def get_period_id( + time: _Instant, + tzinfo: datetime.tzinfo | None = None, + type: Literal['selection'] | None = None, + locale: Locale | str | None = LC_TIME, +) -> str: """ Get the day period ID for a given time. This ID can be used as a key for the period name dictionary. + >>> from datetime import time >>> get_period_names(locale="de")[get_period_id(time(7, 42), locale="de")] u'Morgen' @@ -1245,7 +1304,11 @@ class ParseError(ValueError): pass -def parse_date(string: str, locale: Locale | str | None = LC_TIME, format: _PredefinedTimeFormat = 'medium') -> date: +def parse_date( + string: str, + locale: Locale | str | None = LC_TIME, + format: _PredefinedTimeFormat = 'medium', +) -> datetime.date: """Parse a date from a string. This function first tries to interpret the string as ISO-8601 @@ -1275,7 +1338,7 @@ def parse_date(string: str, locale: Locale | str | None = LC_TIME, format: _Pred string, flags=re.ASCII) # allow only ASCII digits if iso_alike: try: - return date(*map(int, iso_alike.groups())) + return datetime.date(*map(int, iso_alike.groups())) except ValueError: pass # a locale format might fit better, so let's continue @@ -1302,10 +1365,14 @@ def parse_date(string: str, locale: Locale | str | None = LC_TIME, format: _Pred day = int(numbers[indexes['D']]) if month > 12: month, day = day, month - return date(year, month, day) + return datetime.date(year, month, day) -def parse_time(string: str, locale: Locale | str | None = LC_TIME, format: _PredefinedTimeFormat = 'medium') -> time: +def parse_time( + string: str, + locale: Locale | str | None = LC_TIME, + format: _PredefinedTimeFormat = 'medium', +) -> datetime.time: """Parse a time from a string. This function uses the time format for the locale as a hint to determine @@ -1352,7 +1419,7 @@ def parse_time(string: str, locale: Locale | str | None = LC_TIME, format: _Pred minute = int(numbers[indexes['M']]) if len(numbers) > 2: second = int(numbers[indexes['S']]) - return time(hour, minute, second) + return datetime.time(hour, minute, second) class DateTimePattern: @@ -1375,9 +1442,9 @@ def __mod__(self, other: DateTimeFormat) -> str: def apply( self, - datetime: date | time, + datetime: datetime.date | datetime.time, locale: Locale | str | None, - reference_date: date | None = None + reference_date: datetime.date | None = None, ) -> str: return self % DateTimeFormat(datetime, locale, reference_date) @@ -1386,12 +1453,12 @@ class DateTimeFormat: def __init__( self, - value: date | time, + value: datetime.date | datetime.time, locale: Locale | str, - reference_date: date | None = None - ): - assert isinstance(value, (date, datetime, time)) - if isinstance(value, (datetime, time)) and value.tzinfo is None: + reference_date: datetime.date | None = None + ) -> None: + assert isinstance(value, (datetime.date, datetime.datetime, datetime.time)) + if isinstance(value, (datetime.datetime, datetime.time)) and value.tzinfo is None: value = value.replace(tzinfo=UTC) self.value = value self.locale = Locale.parse(locale) @@ -1500,14 +1567,14 @@ def format_week(self, char: str, num: int) -> str: day_of_year = self.get_day_of_year() week = self.get_week_number(day_of_year) if week == 0: - date = self.value - timedelta(days=day_of_year) + date = self.value - datetime.timedelta(days=day_of_year) week = self.get_week_number(self.get_day_of_year(date), date.weekday()) return self.format(week, num) else: # week of month week = self.get_week_number(self.value.day) if week == 0: - date = self.value - timedelta(days=self.value.day) + date = self.value - datetime.timedelta(days=self.value.day) week = self.get_week_number(date.day, date.weekday()) return str(week) @@ -1515,6 +1582,7 @@ def format_weekday(self, char: str = 'E', num: int = 4) -> str: """ Return weekday from parsed datetime according to format pattern. + >>> from datetime import date >>> format = DateTimeFormat(date(2016, 2, 28), Locale.parse('en_US')) >>> format.format_weekday() u'Sunday' @@ -1561,6 +1629,7 @@ def format_period(self, char: str, num: int) -> str: """ Return period from parsed datetime according to format pattern. + >>> from datetime import datetime, time >>> format = DateTimeFormat(time(13, 42), 'fi_FI') >>> format.format_period('a', 1) u'ip.' @@ -1621,7 +1690,7 @@ def format_timezone(self, char: str, num: int) -> str: # variants (summer/standard time) value = self.value if self.reference_date: - value = datetime.combine(self.reference_date, self.value) + value = datetime.datetime.combine(self.reference_date, self.value) if char == 'z': return get_timezone_name(value, width, locale=self.locale) @@ -1667,7 +1736,7 @@ def format_timezone(self, char: str, num: int) -> str: def format(self, value: SupportsInt, length: int) -> str: return '%0*d' % (length, value) - def get_day_of_year(self, date: date | None = None) -> int: + def get_day_of_year(self, date: datetime.date | None = None) -> int: if date is None: date = self.value return (date - date.replace(month=1, day=1)).days + 1 @@ -1680,12 +1749,10 @@ def get_week_number(self, day_of_period: int, day_of_week: int | None = None) -> first week of the period is so short that it actually counts as the last week of the previous period, this function will return 0. - >>> format = DateTimeFormat(date(2006, 1, 8), Locale.parse('de_DE')) - >>> format.get_week_number(6) + >>> date = datetime.date(2006, 1, 8) + >>> DateTimeFormat(date, 'de_DE').get_week_number(6) 1 - - >>> format = DateTimeFormat(date(2006, 1, 8), Locale.parse('en_US')) - >>> format.get_week_number(6) + >>> DateTimeFormat(date, 'en_US').get_week_number(6) 2 :param day_of_period: the number of the day in the period (usually @@ -1710,7 +1777,7 @@ def get_week_number(self, day_of_period: int, day_of_week: int | None = None) -> # we must count from zero.For example the above calculation gives week 53 # for 2018-12-31. By iso-calender definition 2018 has a max of 52 # weeks, thus the weeknumber must be 53-52=1. - max_weeks = date(year=self.value.year, day=28, month=12).isocalendar()[1] + max_weeks = datetime.date(year=self.value.year, day=28, month=12).isocalendar()[1] if week_number > max_weeks: week_number -= max_weeks diff --git a/babel/localtime/__init__.py b/babel/localtime/__init__.py index c7f214aa8..9d227c7b1 100644 --- a/babel/localtime/__init__.py +++ b/babel/localtime/__init__.py @@ -11,7 +11,7 @@ import sys import time -from datetime import datetime, timedelta, tzinfo +import datetime from threading import RLock if sys.platform == 'win32': @@ -23,34 +23,34 @@ _cached_tz = None _cache_lock = RLock() -STDOFFSET = timedelta(seconds=-time.timezone) +STDOFFSET = datetime.timedelta(seconds=-time.timezone) if time.daylight: - DSTOFFSET = timedelta(seconds=-time.altzone) + DSTOFFSET = datetime.timedelta(seconds=-time.altzone) else: DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET -ZERO = timedelta(0) +ZERO = datetime.timedelta(0) -class _FallbackLocalTimezone(tzinfo): +class _FallbackLocalTimezone(datetime.tzinfo): - def utcoffset(self, dt: datetime) -> timedelta: + def utcoffset(self, dt: datetime.datetime) -> datetime.timedelta: if self._isdst(dt): return DSTOFFSET else: return STDOFFSET - def dst(self, dt: datetime) -> timedelta: + def dst(self, dt: datetime.datetime) -> datetime.timedelta: if self._isdst(dt): return DSTDIFF else: return ZERO - def tzname(self, dt: datetime) -> str: + def tzname(self, dt: datetime.datetime) -> str: return time.tzname[self._isdst(dt)] - def _isdst(self, dt: datetime) -> bool: + def _isdst(self, dt: datetime.datetime) -> bool: tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1) @@ -59,7 +59,7 @@ def _isdst(self, dt: datetime) -> bool: return tt.tm_isdst > 0 -def get_localzone() -> tzinfo: +def get_localzone() -> datetime.tzinfo: """Returns the current underlying local timezone object. Generally this function does not need to be used, it's a better idea to use the :data:`LOCALTZ` singleton instead. diff --git a/babel/localtime/_unix.py b/babel/localtime/_unix.py index 319c8cfbc..89b461a01 100644 --- a/babel/localtime/_unix.py +++ b/babel/localtime/_unix.py @@ -1,7 +1,7 @@ import os import re -from datetime import tzinfo +import datetime from babel.localtime._helpers import ( _get_tzinfo_from_file, @@ -9,7 +9,7 @@ _get_tzinfo, ) -def _tz_from_env(tzenv: str) -> tzinfo: +def _tz_from_env(tzenv: str) -> datetime.tzinfo: if tzenv[0] == ':': tzenv = tzenv[1:] @@ -21,7 +21,7 @@ def _tz_from_env(tzenv: str) -> tzinfo: return _get_tzinfo_or_raise(tzenv) -def _get_localzone(_root: str = '/') -> tzinfo: +def _get_localzone(_root: str = '/') -> datetime.tzinfo: """Tries to find the local timezone configuration. This method prefers finding the timezone name and passing that to zoneinfo or pytz, over passing in the localtime file, as in the later diff --git a/babel/localtime/_win32.py b/babel/localtime/_win32.py index 3d7e0d512..42f819a21 100644 --- a/babel/localtime/_win32.py +++ b/babel/localtime/_win32.py @@ -5,7 +5,7 @@ except ImportError: winreg = None -from datetime import tzinfo +import datetime from babel.core import get_global from babel.localtime._helpers import _get_tzinfo_or_raise from typing import Any, Dict, cast @@ -89,7 +89,7 @@ def get_localzone_name() -> str: return timezone -def _get_localzone() -> tzinfo: +def _get_localzone() -> datetime.tzinfo: if winreg is None: raise LookupError( 'Runtime support not available') diff --git a/babel/messages/catalog.py b/babel/messages/catalog.py index 0801de371..4486bec4e 100644 --- a/babel/messages/catalog.py +++ b/babel/messages/catalog.py @@ -13,7 +13,7 @@ from collections import OrderedDict from collections.abc import Generator, Iterable, Iterator -from datetime import datetime, time as time_ +import datetime from difflib import get_close_matches from email import message_from_string from copy import copy @@ -45,10 +45,10 @@ ''', re.VERBOSE) -def _parse_datetime_header(value: str) -> datetime: +def _parse_datetime_header(value: str) -> datetime.datetime: match = re.match(r'^(?P.*?)(?P[+-]\d{4})?$', value) - dt = datetime.strptime(match.group('datetime'), '%Y-%m-%d %H:%M') + dt = datetime.datetime.strptime(match.group('datetime'), '%Y-%m-%d %H:%M') # Separate the offset into a sign component, hours, and # minutes tzoffset = match.group('tzoffset') @@ -261,8 +261,8 @@ def __init__( version: str | None = None, copyright_holder: str | None = None, msgid_bugs_address: str | None = None, - creation_date: datetime | str | None = None, - revision_date: datetime | time_ | float | str | None = None, + creation_date: datetime.datetime | str | None = None, + revision_date: datetime.datetime | datetime.time | float | str | None = None, last_translator: str | None = None, language_team: str | None = None, charset: str | None = None, @@ -306,13 +306,13 @@ def __init__( self.charset = charset or 'utf-8' if creation_date is None: - creation_date = datetime.now(LOCALTZ) - elif isinstance(creation_date, datetime) and not creation_date.tzinfo: + creation_date = datetime.datetime.now(LOCALTZ) + elif isinstance(creation_date, datetime.datetime) and not creation_date.tzinfo: creation_date = creation_date.replace(tzinfo=LOCALTZ) self.creation_date = creation_date if revision_date is None: revision_date = 'YEAR-MO-DA HO:MI+ZONE' - elif isinstance(revision_date, datetime) and not revision_date.tzinfo: + elif isinstance(revision_date, datetime.datetime) and not revision_date.tzinfo: revision_date = revision_date.replace(tzinfo=LOCALTZ) self.revision_date = revision_date self.fuzzy = fuzzy @@ -354,7 +354,7 @@ def _get_locale_identifier(self) -> str | None: def _get_header_comment(self) -> str: comment = self._header_comment - year = datetime.now(LOCALTZ).strftime('%Y') + year = datetime.datetime.now(LOCALTZ).strftime('%Y') if hasattr(self.revision_date, 'strftime'): year = self.revision_date.strftime('%Y') comment = comment.replace('PROJECT', self.project) \ @@ -409,7 +409,7 @@ def _get_mime_headers(self) -> list[tuple[str, str]]: headers.append(('POT-Creation-Date', format_datetime(self.creation_date, 'yyyy-MM-dd HH:mmZ', locale='en'))) - if isinstance(self.revision_date, (datetime, time_, int, float)): + if isinstance(self.revision_date, (datetime.datetime, datetime.time, int, float)): headers.append(('PO-Revision-Date', format_datetime(self.revision_date, 'yyyy-MM-dd HH:mmZ', locale='en'))) @@ -481,6 +481,7 @@ def _set_mime_headers(self, headers: Iterable[tuple[str, str]]) -> None: Here's an example of the output for such a catalog template: >>> from babel.dates import UTC + >>> from datetime import datetime >>> created = datetime(1990, 4, 1, 15, 30, tzinfo=UTC) >>> catalog = Catalog(project='Foobar', version='1.0', ... creation_date=created) diff --git a/babel/messages/frontend.py b/babel/messages/frontend.py index c42cdc2e2..c7e921d09 100644 --- a/babel/messages/frontend.py +++ b/babel/messages/frontend.py @@ -18,7 +18,7 @@ import tempfile from collections import OrderedDict from configparser import RawConfigParser -from datetime import datetime +import datetime from io import StringIO from babel import __version__ as VERSION @@ -662,7 +662,7 @@ def run(self): catalog = read_po(infile, locale=self.locale) catalog.locale = self._locale - catalog.revision_date = datetime.now(LOCALTZ) + catalog.revision_date = datetime.datetime.now(LOCALTZ) catalog.fuzzy = False with open(self.output_file, 'wb') as outfile: @@ -818,7 +818,7 @@ def run(self): catalog = read_po(infile, locale=self.locale) catalog.locale = self._locale - catalog.revision_date = datetime.now(LOCALTZ) + catalog.revision_date = datetime.datetime.now(LOCALTZ) catalog.fuzzy = False with open(filename, 'wb') as outfile: diff --git a/babel/numbers.py b/babel/numbers.py index 73155c4cf..95e9d2af0 100644 --- a/babel/numbers.py +++ b/babel/numbers.py @@ -23,7 +23,7 @@ import re from typing import TYPE_CHECKING, Any, overload import warnings -from datetime import date as date_, datetime as datetime_ +import datetime from babel.core import Locale, default_locale, get_global from babel.localedata import LocaleDataDict @@ -200,8 +200,8 @@ def get_currency_unit_pattern( @overload def get_territory_currencies( territory: str, - start_date: date_ | None = ..., - end_date: date_ | None = ..., + start_date: datetime.date | None = ..., + end_date: datetime.date | None = ..., tender: bool = ..., non_tender: bool = ..., include_details: Literal[False] = ..., @@ -212,8 +212,8 @@ def get_territory_currencies( @overload def get_territory_currencies( territory: str, - start_date: date_ | None = ..., - end_date: date_ | None = ..., + start_date: datetime.date | None = ..., + end_date: datetime.date | None = ..., tender: bool = ..., non_tender: bool = ..., include_details: Literal[True] = ..., @@ -223,8 +223,8 @@ def get_territory_currencies( def get_territory_currencies( territory: str, - start_date: date_ | None = None, - end_date: date_ | None = None, + start_date: datetime.date | None = None, + end_date: datetime.date | None = None, tender: bool = True, non_tender: bool = False, include_details: bool = False, @@ -280,12 +280,12 @@ def get_territory_currencies( """ currencies = get_global('territory_currencies') if start_date is None: - start_date = date_.today() - elif isinstance(start_date, datetime_): + start_date = datetime.date.today() + elif isinstance(start_date, datetime.datetime): start_date = start_date.date() if end_date is None: end_date = start_date - elif isinstance(end_date, datetime_): + elif isinstance(end_date, datetime.datetime): end_date = end_date.date() curs = currencies.get(territory.upper(), ()) @@ -298,9 +298,9 @@ def _is_active(start, end): result = [] for currency_code, start, end, is_tender in curs: if start: - start = date_(*start) + start = datetime.date(*start) if end: - end = date_(*end) + end = datetime.date(*end) if ((is_tender and tender) or (not is_tender and non_tender)) and _is_active(start, end): if include_details: diff --git a/babel/support.py b/babel/support.py index 7477ee10c..3fe238a5a 100644 --- a/babel/support.py +++ b/babel/support.py @@ -16,14 +16,8 @@ import gettext import locale import os +import datetime from collections.abc import Iterator -from datetime import ( - date as _date, - datetime as _datetime, - time as _time, - timedelta as _timedelta, - tzinfo -) from typing import TYPE_CHECKING, Any, Callable from babel.core import Locale @@ -52,7 +46,7 @@ class Format: u'1.234' """ - def __init__(self, locale: Locale | str, tzinfo: tzinfo | None = None) -> None: + def __init__(self, locale: Locale | str, tzinfo: datetime.tzinfo | None = None) -> None: """Initialize the formatter. :param locale: the locale identifier or `Locale` instance @@ -61,7 +55,11 @@ def __init__(self, locale: Locale | str, tzinfo: tzinfo | None = None) -> None: self.locale = Locale.parse(locale) self.tzinfo = tzinfo - def date(self, date: _date | None = None, format: _PredefinedTimeFormat | str = 'medium') -> str: + def date( + self, + date: datetime.date | None = None, + format: _PredefinedTimeFormat | str = 'medium', + ) -> str: """Return a date formatted according to the given pattern. >>> from datetime import date @@ -71,7 +69,11 @@ def date(self, date: _date | None = None, format: _PredefinedTimeFormat | str = """ return format_date(date, format, locale=self.locale) - def datetime(self, datetime: _date | None = None, format: _PredefinedTimeFormat | str = 'medium') -> str: + def datetime( + self, + datetime: datetime.date | datetime.datetime | None = None, + format: _PredefinedTimeFormat | str = 'medium', + ) -> str: """Return a date and time formatted according to the given pattern. >>> from datetime import datetime @@ -79,10 +81,13 @@ def datetime(self, datetime: _date | None = None, format: _PredefinedTimeFormat >>> fmt.datetime(datetime(2007, 4, 1, 15, 30)) u'Apr 1, 2007, 11:30:00 AM' """ - return format_datetime(datetime, format, tzinfo=self.tzinfo, - locale=self.locale) + return format_datetime(datetime, format, tzinfo=self.tzinfo, locale=self.locale) - def time(self, time: _time | _datetime | None = None, format: _PredefinedTimeFormat | str = 'medium') -> str: + def time( + self, + time: datetime.time | datetime.datetime | None = None, + format: _PredefinedTimeFormat | str = 'medium', + ) -> str: """Return a time formatted according to the given pattern. >>> from datetime import datetime @@ -94,7 +99,7 @@ def time(self, time: _time | _datetime | None = None, format: _PredefinedTimeFor def timedelta( self, - delta: _timedelta | int, + delta: datetime.timedelta | int, granularity: Literal["year", "month", "week", "day", "hour", "minute", "second"] = "second", threshold: float = 0.85, format: Literal["narrow", "short", "medium", "long"] = "long", diff --git a/babel/util.py b/babel/util.py index fb4870d66..d25ec533d 100644 --- a/babel/util.py +++ b/babel/util.py @@ -17,7 +17,7 @@ from babel import localtime, dates from collections.abc import Generator, Iterable -from datetime import datetime as datetime_, timedelta, tzinfo +import datetime from typing import IO, Any, TypeVar missing = object() @@ -225,12 +225,12 @@ def wraptext(text: str, width: int = 70, initial_indent: str = '', subsequent_in odict = collections.OrderedDict -class FixedOffsetTimezone(tzinfo): +class FixedOffsetTimezone(datetime.tzinfo): """Fixed offset in minutes east from UTC.""" def __init__(self, offset: float, name: str | None = None) -> None: - self._offset = timedelta(minutes=offset) + self._offset = datetime.timedelta(minutes=offset) if name is None: name = 'Etc/GMT%+d' % offset self.zone = name @@ -241,13 +241,13 @@ def __str__(self) -> str: def __repr__(self) -> str: return f'' - def utcoffset(self, dt: datetime_) -> timedelta: + def utcoffset(self, dt: datetime.datetime) -> datetime.timedelta: return self._offset - def tzname(self, dt: datetime_) -> str: + def tzname(self, dt: datetime.datetime) -> str: return self.zone - def dst(self, dt: datetime_) -> timedelta: + def dst(self, dt: datetime.datetime) -> datetime.timedelta: return ZERO From fd97d5a8749c920edd798d6550ebea22a87590f3 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Wed, 18 Jan 2023 10:57:59 +0200 Subject: [PATCH 2/2] Update babel/support.py Co-authored-by: Jonah Lawrence --- babel/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/babel/support.py b/babel/support.py index 3fe238a5a..da111fc26 100644 --- a/babel/support.py +++ b/babel/support.py @@ -71,7 +71,7 @@ def date( def datetime( self, - datetime: datetime.date | datetime.datetime | None = None, + datetime: datetime.date | None = None, format: _PredefinedTimeFormat | str = 'medium', ) -> str: """Return a date and time formatted according to the given pattern.