Skip to content

Commit

Permalink
Fix typing of Timezone and ZoneInfo (#670)
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanforbes authored Nov 24, 2022
1 parent 9d06ff3 commit 46b27c1
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 19 deletions.
2 changes: 1 addition & 1 deletion pendulum/_extensions/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 5 additions & 5 deletions pendulum/formatting/formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"])
Expand All @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion pendulum/parsing/iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pendulum/tz/local_timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand All @@ -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:
Expand Down
27 changes: 20 additions & 7 deletions pendulum/tz/timezone.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions pendulum/utils/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

0 comments on commit 46b27c1

Please sign in to comment.