diff --git a/pendulum/tz/timezone.py b/pendulum/tz/timezone.py index a79242ed..e10ec00a 100644 --- a/pendulum/tz/timezone.py +++ b/pendulum/tz/timezone.py @@ -105,7 +105,7 @@ def _normalize( # We set the fold attribute for later if dst_rule == POST_TRANSITION: fold = 1 - else: + elif transition.previous is not None: transition = transition.previous if transition.is_ambiguous(sec): @@ -149,7 +149,7 @@ def _convert(self, dt): # type: (datetime) -> datetime transition = dt.tzinfo._lookup_transition(stamp) offset = transition.ttype.offset - if stamp < transition.local: + if stamp < transition.local and transition.previous is not None: if ( transition.previous.is_ambiguous(stamp) and getattr(dt, "fold", 1) == 0 @@ -161,7 +161,7 @@ def _convert(self, dt): # type: (datetime) -> datetime stamp -= offset transition = self._lookup_transition(stamp, is_utc=True) - if stamp < transition.at: + if stamp < transition.at and transition.previous is not None: transition = transition.previous offset = transition.ttype.offset @@ -254,7 +254,7 @@ def _get_transition(self, dt): # type: (datetime) -> Transition transition = self._lookup_transition(stamp) - if stamp < transition.local: + if stamp < transition.local and transition.previous is not None: fold = getattr(dt, "fold", 1) if transition.is_ambiguous(stamp): if fold == 0: @@ -270,7 +270,7 @@ def fromutc(self, dt): # type: (datetime) -> datetime stamp = timestamp(dt) transition = self._lookup_transition(stamp, is_utc=True) - if stamp < transition.at: + if stamp < transition.at and transition.previous is not None: transition = transition.previous stamp += transition.ttype.offset diff --git a/pendulum/tz/zoneinfo/transition.py b/pendulum/tz/zoneinfo/transition.py index 1ba93a46..278f7cd6 100644 --- a/pendulum/tz/zoneinfo/transition.py +++ b/pendulum/tz/zoneinfo/transition.py @@ -1,5 +1,5 @@ from datetime import timedelta -from typing import Union +from typing import Union, Optional from .transition_type import TransitionType @@ -51,7 +51,7 @@ def ttype(self): # type: () -> TransitionType return self._ttype @property - def previous(self): # type: () -> Transition + def previous(self): # type: () -> Optional[Transition] return self._previous @property @@ -67,7 +67,10 @@ def is_missing(self, stamp): # type: (int) -> bool def utcoffset(self): # type: () -> timedelta return self._utcoffset - def __contains__(self, stamp): # type: () -> bool + def __contains__(self, stamp): # type: (int) -> bool + if self.previous is None: + return stamp < self.local + return self.previous.local <= stamp < self.local def __repr__(self): # type: () -> str diff --git a/tests/tz/test_timezone.py b/tests/tz/test_timezone.py index 7d407a8d..31d8c3dc 100644 --- a/tests/tz/test_timezone.py +++ b/tests/tz/test_timezone.py @@ -303,6 +303,12 @@ def test_utcoffset(): assert utcoffset == timedelta(0, -18000) +def test_utcoffset_pre_transition(): + tz = pendulum.timezone("America/Chicago") + utcoffset = tz.utcoffset(datetime(1883, 11, 18)) + assert utcoffset == timedelta(days=-1, seconds=64800) + + def test_dst(): tz = pendulum.timezone("Europe/Amsterdam") dst = tz.dst(datetime(1940, 7, 1))