diff --git a/pendulum/_extensions/helpers.py b/pendulum/_extensions/helpers.py index 01066a30..e9839507 100644 --- a/pendulum/_extensions/helpers.py +++ b/pendulum/_extensions/helpers.py @@ -353,7 +353,7 @@ def _get_tzinfo_name(tzinfo: datetime.tzinfo | None) -> str | None: if hasattr(tzinfo, "key"): # zoneinfo timezone - return cast(str, cast(zoneinfo.ZoneInfo, tzinfo).key) + return cast(zoneinfo.ZoneInfo, tzinfo).key elif hasattr(tzinfo, "name"): # Pendulum timezone return cast(Timezone, tzinfo).name diff --git a/pendulum/formatting/formatter.py b/pendulum/formatting/formatter.py index f91d5d92..032daaaa 100644 --- a/pendulum/formatting/formatter.py +++ b/pendulum/formatting/formatter.py @@ -448,7 +448,7 @@ def _check_parsed( if parsed["quarter"] is not None: if validated["year"] is not None: - dt = pendulum.datetime(validated["year"], 1, 1) + dt = pendulum.datetime(cast(int, validated["year"]), 1, 1) else: dt = now @@ -476,8 +476,8 @@ def _check_parsed( if parsed["day_of_week"] is not None: dt = pendulum.datetime( cast(int, validated["year"]), - validated["month"] or now.month, - validated["day"] or now.day, + cast(int, validated["month"]) or now.month, + cast(int, validated["day"]) or now.day, ) dt = dt.start_of("week").subtract(days=1) dt = dt.next(parsed["day_of_week"]) @@ -502,9 +502,9 @@ def _check_parsed( raise ValueError("Invalid date") pm = parsed["meridiem"] == "pm" - validated["hour"] %= 12 + validated["hour"] %= 12 # type: ignore if pm: - validated["hour"] += 12 + validated["hour"] += 12 # type: ignore if validated["month"] is None: if parsed["year"] is not None: diff --git a/pendulum/parsing/iso8601.py b/pendulum/parsing/iso8601.py index 907cf13f..ba2bba39 100644 --- a/pendulum/parsing/iso8601.py +++ b/pendulum/parsing/iso8601.py @@ -17,6 +17,7 @@ from pendulum.parsing.exceptions import ParserError from pendulum.tz.timezone import UTC from pendulum.tz.timezone import FixedTimezone +from pendulum.tz.timezone import Timezone ISO8601_DT = re.compile( # Date (optional) # noqa: E800 @@ -107,7 +108,7 @@ def parse_iso8601( minute = 0 second = 0 microsecond = 0 - tzinfo: FixedTimezone | None = None + tzinfo: FixedTimezone | Timezone | None = None if m.group("date"): # A date has been specified diff --git a/pendulum/tz/local_timezone.py b/pendulum/tz/local_timezone.py index 41cf81bf..28b07ba7 100644 --- a/pendulum/tz/local_timezone.py +++ b/pendulum/tz/local_timezone.py @@ -239,7 +239,7 @@ def _get_unix_timezone(_root: str = "/") -> Timezone: continue with open(tzpath, "rb") as f: - return cast(Timezone, Timezone.from_file(f)) + return Timezone.from_file(f) raise RuntimeError("Unable to find any timezone configuration") @@ -251,7 +251,7 @@ def _tz_from_env(tzenv: str) -> Timezone: # TZ specifies a file if os.path.isfile(tzenv): with open(tzenv, "rb") as f: - return cast(Timezone, Timezone.from_file(f)) + return Timezone.from_file(f) # TZ specifies a zoneinfo zone. try: diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index f689004e..e7e84af9 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -42,7 +42,7 @@ def datetime( raise NotImplementedError -class Timezone(zoneinfo.ZoneInfo, PendulumTimezone): # type: ignore[misc] +class Timezone(zoneinfo.ZoneInfo, PendulumTimezone): """ Represents a named timezone. @@ -54,13 +54,13 @@ class Timezone(zoneinfo.ZoneInfo, PendulumTimezone): # type: ignore[misc] def __new__(cls, key: str) -> Timezone: try: - return cast(Timezone, super().__new__(cls, key)) + return super().__new__(cls, key) # type: ignore[call-arg] except zoneinfo.ZoneInfoNotFoundError: raise InvalidTimezone(key) @property def name(self) -> str: - return cast(str, self.key) + return self.key def convert( self, dt: datetime_.datetime, raise_on_unknown_times: bool = False @@ -86,11 +86,24 @@ def convert( '2013-03-30T21:30:00-04:00' """ if dt.tzinfo is None: - offset_before = ( - self.utcoffset(dt.replace(fold=0)) if dt.fold else self.utcoffset(dt) + # Technically, utcoffset() can return None, but none of the zone information + # in tzdata sets _tti_before to None. This can be checked with the following + # code: + # + # >>> import zoneinfo + # >>> from zoneinfo._zoneinfo import ZoneInfo + # + # >>> for tzname in zoneinfo.available_timezones(): + # >>> if ZoneInfo(tzname)._tti_before is None: + # >>> print(tzname) + + offset_before = cast( + datetime_.timedelta, + (self.utcoffset(dt.replace(fold=0)) if dt.fold else self.utcoffset(dt)), ) - offset_after = ( - self.utcoffset(dt) if dt.fold else self.utcoffset(dt.replace(fold=1)) + offset_after = cast( + datetime_.timedelta, + (self.utcoffset(dt) if dt.fold else self.utcoffset(dt.replace(fold=1))), ) if offset_after > offset_before: diff --git a/pendulum/utils/_compat.py b/pendulum/utils/_compat.py index 8f32f9e7..81b0a308 100644 --- a/pendulum/utils/_compat.py +++ b/pendulum/utils/_compat.py @@ -5,9 +5,9 @@ PYPY = hasattr(sys, "pypy_version_info") PY38 = sys.version_info[:2] >= (3, 8) -try: +if sys.version_info < (3, 9): from backports import zoneinfo -except ImportError: - import zoneinfo # type: ignore[no-redef] +else: + import zoneinfo __all__ = ["zoneinfo"]