Skip to content

Commit

Permalink
🤏 Refactoring and deduplication
Browse files Browse the repository at this point in the history
  • Loading branch information
ariebovenberg committed Jan 20, 2024
1 parent abdf398 commit 29d502e
Showing 1 changed file with 22 additions and 40 deletions.
62 changes: 22 additions & 40 deletions src/whenever/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ def as_offset(self, offset: timedelta | None = None, /) -> OffsetDateTime:
The result will always represent the same moment in time.
"""

@abstractmethod
def as_zoned(self, tz: str, /) -> ZonedDateTime:
"""Convert into an equivalent ZonedDateTime.
The result will always represent the same moment in time.
Expand All @@ -291,12 +290,15 @@ def as_zoned(self, tz: str, /) -> ZonedDateTime:
~zoneinfo.ZoneInfoNotFoundError
If the timezone name is not found in the IANA database.
"""
return ZonedDateTime._from_py_unchecked(
self._py_dt.astimezone(ZoneInfo(tz))
)

@abstractmethod
def as_local(self) -> LocalDateTime:
"""Convert into a an equivalent LocalDateTime.
The result will always represent the same moment in time.
"""
return LocalDateTime._from_py_unchecked(_to_local(self._py_dt))

def naive(self) -> NaiveDateTime:
"""Convert into a naive datetime, dropping all timezone information"""
Expand Down Expand Up @@ -660,14 +662,6 @@ def as_offset(self, offset: timedelta | None = None, /) -> OffsetDateTime:
)
)

def as_zoned(self, tz: str, /) -> ZonedDateTime:
return ZonedDateTime._from_py_unchecked(
self._py_dt.astimezone(ZoneInfo(tz))
)

def as_local(self) -> LocalDateTime:
return LocalDateTime._from_py_unchecked(_to_local(self._py_dt))

@classmethod
def from_naive(cls, d: NaiveDateTime, /) -> UTCDateTime:
"""Create an instance from a naive datetime."""
Expand Down Expand Up @@ -883,11 +877,10 @@ def __sub__(self, other: AwareDateTime) -> timedelta:
if isinstance(other, (UTCDateTime, OffsetDateTime, ZonedDateTime)):
return self._py_dt - other._py_dt
elif isinstance(other, LocalDateTime):
return self._py_dt - other._py_dt.astimezone(None)
return self._py_dt - other._py_dt.astimezone()
return NotImplemented

def as_utc(self) -> UTCDateTime:
"""Convert into an equivalent UTCDateTime"""
return UTCDateTime._from_py_unchecked(self._py_dt.astimezone(_UTC))

@overload
Expand All @@ -907,14 +900,6 @@ def as_offset(self, offset: timedelta | None = None, /) -> OffsetDateTime:
)
)

def as_zoned(self, tz: str, /) -> ZonedDateTime:
return ZonedDateTime._from_py_unchecked(
self._py_dt.astimezone(ZoneInfo(tz))
)

def as_local(self) -> LocalDateTime:
return LocalDateTime._from_py_unchecked(_to_local(self._py_dt))

@classmethod
def from_naive(
cls, d: NaiveDateTime, /, offset: timedelta
Expand Down Expand Up @@ -1229,10 +1214,10 @@ def __add__(self, delta: timedelta) -> ZonedDateTime:
-------
.. code-block:: python
d = ZonedDateTime(2023, 10, 29, 23, 12, tz="Europe/Amsterdam", disambiguate="earlier")
d = ZonedDateTime(2023, 10, 28, 12, tz="Europe/Amsterdam", disambiguate="earlier")
# one hour skipped due to DST transition
d + timedelta(hours=24) # 2023-10-30T22:12:00+01:00[Europe/Amsterdam]
d + timedelta(hours=24) # 2023-10-29T11:00:00+01:00[Europe/Amsterdam]
"""
if not isinstance(delta, timedelta):
return NotImplemented
Expand Down Expand Up @@ -1266,7 +1251,7 @@ def __sub__(
if isinstance(other, (UTCDateTime, OffsetDateTime, ZonedDateTime)):
return self._py_dt.astimezone(_UTC) - other._py_dt
elif isinstance(other, LocalDateTime):
return self._py_dt - other._py_dt.astimezone(None)
return self._py_dt - other._py_dt.astimezone()
elif isinstance(other, timedelta):
return self._from_py_unchecked(
(self._py_dt.astimezone(_UTC) - other).astimezone(
Expand All @@ -1284,9 +1269,9 @@ def disambiguated(self) -> bool:
.. code-block:: python
# False: no disambiguation needed
ZonedDateTime(2020, 8, 15, 23, tz="Europe/London", disambiguate="later").is_ambiguous()
ZonedDateTime(2020, 8, 15, 23, tz="Europe/London", disambiguate="later").disambiguated()
# True: disambiguation needed, since 2:15 AM occurs twice
ZonedDateTime(2023, 10, 29, 2, 15, tz="Europe/Amsterdam", disambiguate="later").is_ambiguous()
ZonedDateTime(2023, 10, 29, 2, 15, tz="Europe/Amsterdam", disambiguate="later").disambiguated()
"""
return self._py_dt.astimezone(_UTC) != self._py_dt

Expand All @@ -1312,9 +1297,6 @@ def as_offset(self, offset: timedelta | None = None, /) -> OffsetDateTime:
def as_zoned(self, tz: str, /) -> ZonedDateTime:
return self._from_py_unchecked(self._py_dt.astimezone(ZoneInfo(tz)))

def as_local(self) -> LocalDateTime:
return LocalDateTime._from_py_unchecked(_to_local(self._py_dt))

@classmethod
def from_naive(
cls,
Expand Down Expand Up @@ -1472,7 +1454,7 @@ def __init__(
year, month, day, hour, minute, second, microsecond, fold=fold
)
# If it doesn't survive the UTC roundtrip, it doesn't exist
if dt.astimezone(_UTC).astimezone(None).replace(tzinfo=None) != dt:
if dt.astimezone(_UTC).astimezone().replace(tzinfo=None) != dt:
raise DoesntExistInZone()
if disambiguate == "raise" and dt.astimezone(_UTC) != dt.replace(
fold=1
Expand All @@ -1488,7 +1470,7 @@ def now(cls) -> LocalDateTime:
def canonical_str(self) -> str:
if not self.exists():
raise DoesntExistInZone()
return self._py_dt.astimezone(None).isoformat()
return self._py_dt.astimezone().isoformat()

__str__ = canonical_str

Expand All @@ -1500,9 +1482,9 @@ def from_canonical_str(cls, s: str, /) -> LocalDateTime:
# Determine `fold` from the offset
offset = dt.utcoffset()
dt = dt.replace(tzinfo=None)
if offset != dt.astimezone(None).utcoffset():
if offset != dt.astimezone().utcoffset():
dt = dt.replace(fold=1)
if dt.astimezone(None).utcoffset() != offset:
if dt.astimezone().utcoffset() != offset:
raise InvalidOffsetForZone()
return cls._from_py_unchecked(dt)

Expand Down Expand Up @@ -1532,7 +1514,7 @@ def from_py(cls, d: _datetime, /) -> LocalDateTime:
"Can only create LocalDateTime from a naive datetime, "
f"got datetime with tzinfo={d.tzinfo!r}"
)
if d.astimezone(_UTC).astimezone(None).replace(tzinfo=None) != d:
if d.astimezone(_UTC).astimezone().replace(tzinfo=None) != d:
raise DoesntExistInZone()
return cls._from_py_unchecked(d)

Expand All @@ -1546,7 +1528,7 @@ def __repr__(self) -> str:
def offset(self) -> timedelta:
if not self.exists():
raise DoesntExistInZone()
return self._py_dt.astimezone(None).utcoffset() # type: ignore[return-value]
return self._py_dt.astimezone().utcoffset() # type: ignore[return-value]

if TYPE_CHECKING:

Expand Down Expand Up @@ -1619,7 +1601,7 @@ def replace(self, /, **kwargs) -> LocalDateTime:
fold = kwargs["fold"] = _as_fold(disambiguate)
d = self._py_dt.replace(**kwargs)
utc = d.astimezone(_UTC)
if utc.astimezone(None).replace(tzinfo=None) != d:
if utc.astimezone().replace(tzinfo=None) != d:
raise DoesntExistInZone()
if disambiguate == "raise" and utc != d.replace(
fold=not fold
Expand Down Expand Up @@ -1676,7 +1658,7 @@ def __sub__(
"""
utc = self._py_dt.astimezone(_UTC)
if utc.astimezone(None).replace(tzinfo=None) != self._py_dt:
if utc.astimezone().replace(tzinfo=None) != self._py_dt:
raise DoesntExistInZone()
if isinstance(other, LocalDateTime):
return utc - other._py_dt.astimezone(_UTC)
Expand Down Expand Up @@ -1729,14 +1711,14 @@ def exists(self) -> bool:
d.exists() # False
"""
return (
self._py_dt.astimezone(_UTC).astimezone(None).replace(tzinfo=None)
self._py_dt.astimezone(_UTC).astimezone().replace(tzinfo=None)
== self._py_dt
)

def as_utc(self) -> UTCDateTime:
d = self._py_dt.astimezone(_UTC)
# If the UTC round-trip fails, it means the datetime doesn't exist
if d.astimezone(None).replace(tzinfo=None) != self._py_dt:
if d.astimezone().replace(tzinfo=None) != self._py_dt:
raise DoesntExistInZone()
return UTCDateTime._from_py_unchecked(d)

Expand Down Expand Up @@ -1775,7 +1757,7 @@ def from_naive(
fold = _as_fold(disambiguate)
local = d._py_dt.replace(fold=fold)
utc = local.astimezone(_UTC)
if utc.astimezone(None).replace(tzinfo=None) != local:
if utc.astimezone().replace(tzinfo=None) != local:
raise DoesntExistInZone()
if disambiguate == "raise" and utc != local.replace(
fold=not fold
Expand Down Expand Up @@ -2040,7 +2022,7 @@ def _to_local(d: _datetime) -> _datetime:
# Converting to local time results in a datetime with a fixed UTC
# offset. To find the equivelant naive datetime, removing the
# tzinfo is not enough, we need to determine the ``fold`` value.
offset = d.astimezone(None)
offset = d.astimezone()
naive = offset.replace(tzinfo=None)
if naive.astimezone(_UTC) != offset.astimezone(_UTC):
naive = naive.replace(fold=1)
Expand Down

0 comments on commit 29d502e

Please sign in to comment.