diff --git a/pendulum/__init__.py b/pendulum/__init__.py index 9c019c0f0..e1893cf58 100644 --- a/pendulum/__init__.py +++ b/pendulum/__init__.py @@ -206,27 +206,7 @@ def instance( """ Create a DateTime instance from a datetime one. """ - if not isinstance(dt, _datetime.datetime): - raise ValueError("instance() only accepts datetime objects.") - - if isinstance(dt, DateTime): - return dt - - tz = dt.tzinfo or tz - - if tz is not None: - tz = _safe_timezone(tz, dt=dt) - - return datetime( - dt.year, - dt.month, - dt.day, - dt.hour, - dt.minute, - dt.second, - dt.microsecond, - tz=cast(Union[str, int, Timezone, FixedTimezone, None], tz), - ) + return DateTime._instance(dt, tz) def now(tz: str | Timezone | None = None) -> DateTime: diff --git a/pendulum/datetime.py b/pendulum/datetime.py index 67c1e9202..85bceaa7e 100644 --- a/pendulum/datetime.py +++ b/pendulum/datetime.py @@ -10,6 +10,7 @@ from typing import Callable from typing import ClassVar from typing import Optional +from typing import Union from typing import cast from typing import overload @@ -123,6 +124,37 @@ def create( fold=dt.fold, ) + @classmethod + def _instance( + cls, + dt: datetime.datetime, + tz: str | Timezone | FixedTimezone | datetime.tzinfo | None = UTC, + ) -> Self: + """ + Create a DateTime instance from a datetime one. + """ + if not isinstance(dt, datetime.datetime): + raise ValueError("instance() only accepts datetime objects.") + + if isinstance(dt, cls): + return dt + + tz = dt.tzinfo or tz + + if tz is not None: + tz = pendulum._safe_timezone(tz, dt=dt) + + return cls.create( + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + dt.microsecond, + tz=cast(Union[str, int, Timezone, FixedTimezone, None], tz), + ) + @overload @classmethod def now(cls, tz: datetime.tzinfo | None = None) -> Self: @@ -173,8 +205,8 @@ def today(cls) -> Self: return cls.now() @classmethod - def strptime(cls, time: str, fmt: str) -> DateTime: - return pendulum.instance(datetime.datetime.strptime(time, fmt)) + def strptime(cls, time: str, fmt: str) -> Self: + return cls._instance(datetime.datetime.strptime(time, fmt)) # Getters/Setters @@ -473,19 +505,19 @@ def __repr__(self) -> str: ) # Comparisons - def closest(self, *dts: datetime.datetime) -> DateTime: # type: ignore[override] + def closest(self, *dts: datetime.datetime) -> Self: # type: ignore[override] """ Get the farthest date from the instance. """ - pdts = [pendulum.instance(x) for x in dts] + pdts = [self._instance(x) for x in dts] return min((abs(self - dt), dt) for dt in pdts)[1] - def farthest(self, *dts: datetime.datetime) -> DateTime: # type: ignore[override] + def farthest(self, *dts: datetime.datetime) -> Self: # type: ignore[override] """ Get the farthest date from the instance. """ - pdts = [pendulum.instance(x) for x in dts] + pdts = [self._instance(x) for x in dts] return max((abs(self - dt), dt) for dt in pdts)[1] @@ -508,7 +540,7 @@ def is_long_year(self) -> bool: See link `https://en.wikipedia.org/wiki/ISO_8601#Week_dates`_ """ return ( - DateTime.create(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1] + self.create(self.year, 12, 28, 0, 0, 0, tz=self.tz).isocalendar()[1] == 53 ) @@ -517,7 +549,7 @@ def is_same_day(self, dt: datetime.datetime) -> bool: # type: ignore[override] Checks if the passed in date is the same day as the instance current day. """ - dt = pendulum.instance(dt) + dt = self._instance(dt) return self.to_date_string() == dt.to_date_string() @@ -531,7 +563,7 @@ def is_anniversary( # type: ignore[override] if dt is None: dt = self.now(self.tz) - instance = pendulum.instance(dt) + instance = self._instance(dt) return (self.month, self.day) == (instance.month, instance.day) @@ -1177,7 +1209,7 @@ def __sub__(self, other: datetime.datetime | datetime.timedelta) -> Self | Inter if not isinstance(other, self.__class__): if other.tzinfo is None: - other = pendulum.naive( + other = self.__class__( other.year, other.month, other.day, @@ -1187,7 +1219,7 @@ def __sub__(self, other: datetime.datetime | datetime.timedelta) -> Self | Inter other.microsecond, ) else: - other = pendulum.instance(other) + other = self._instance(other) return other.diff(self, False) @@ -1197,7 +1229,7 @@ def __rsub__(self, other: datetime.datetime) -> Interval: if not isinstance(other, self.__class__): if other.tzinfo is None: - other = pendulum.naive( + other = self.__class__( other.year, other.month, other.day, @@ -1207,7 +1239,7 @@ def __rsub__(self, other: datetime.datetime) -> Interval: other.microsecond, ) else: - other = pendulum.instance(other) + other = self._instance(other) return self.diff(other, False) @@ -1231,20 +1263,18 @@ def __radd__(self, other: datetime.timedelta) -> Self: # Native methods override @classmethod - def fromtimestamp(cls, t: float, tz: datetime.tzinfo | None = None) -> DateTime: + def fromtimestamp(cls, t: float, tz: datetime.tzinfo | None = None) -> Self: tzinfo = pendulum._safe_timezone(tz) - return pendulum.instance( - datetime.datetime.fromtimestamp(t, tz=tzinfo), tz=tzinfo - ) + return cls._instance(datetime.datetime.fromtimestamp(t, tz=tzinfo), tz=tzinfo) @classmethod - def utcfromtimestamp(cls, t: float) -> DateTime: - return pendulum.instance(datetime.datetime.utcfromtimestamp(t), tz=None) + def utcfromtimestamp(cls, t: float) -> Self: + return cls._instance(datetime.datetime.utcfromtimestamp(t), tz=None) @classmethod - def fromordinal(cls, n: int) -> DateTime: - return pendulum.instance(datetime.datetime.fromordinal(n), tz=None) + def fromordinal(cls, n: int) -> Self: + return cls._instance(datetime.datetime.fromordinal(n), tz=None) @classmethod def combine( @@ -1252,8 +1282,8 @@ def combine( date: datetime.date, time: datetime.time, tzinfo: datetime.tzinfo | None = None, - ) -> DateTime: - return pendulum.instance(datetime.datetime.combine(date, time), tz=tzinfo) + ) -> Self: + return cls._instance(datetime.datetime.combine(date, time), tz=tzinfo) def astimezone(self, tz: datetime.tzinfo | None = None) -> Self: dt = super().astimezone(tz) @@ -1312,11 +1342,11 @@ def replace( minute, second, microsecond, - tz=tzinfo, + tzinfo=tzinfo, fold=fold, ) - def __getnewargs__(self) -> tuple[DateTime]: + def __getnewargs__(self) -> tuple[Self]: return (self,) def _getstate( @@ -1336,14 +1366,14 @@ def _getstate( def __reduce__( self, ) -> tuple[ - type[DateTime], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None] + type[Self], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None] ]: return self.__reduce_ex__(2) def __reduce_ex__( self, protocol: SupportsIndex ) -> tuple[ - type[DateTime], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None] + type[Self], tuple[int, int, int, int, int, int, int, datetime.tzinfo | None] ]: return self.__class__, self._getstate(protocol)