123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- from __future__ import annotations
- import datetime
- from datetime import time
- from datetime import timedelta
- from typing import TYPE_CHECKING
- from typing import Optional
- from typing import cast
- from typing import overload
- import pendulum
- from pendulum.constants import SECS_PER_HOUR
- from pendulum.constants import SECS_PER_MIN
- from pendulum.constants import USECS_PER_SEC
- from pendulum.duration import AbsoluteDuration
- from pendulum.duration import Duration
- from pendulum.mixins.default import FormattableMixin
- from pendulum.tz.timezone import UTC
- if TYPE_CHECKING:
- from typing_extensions import Literal
- from typing_extensions import Self
- from typing_extensions import SupportsIndex
- from pendulum.tz.timezone import FixedTimezone
- from pendulum.tz.timezone import Timezone
- class Time(FormattableMixin, time):
- """
- Represents a time instance as hour, minute, second, microsecond.
- """
- @classmethod
- def instance(
- cls, t: time, tz: str | Timezone | FixedTimezone | datetime.tzinfo | None = UTC
- ) -> Self:
- tz = t.tzinfo or tz
- if tz is not None:
- tz = pendulum._safe_timezone(tz)
- return cls(t.hour, t.minute, t.second, t.microsecond, tzinfo=tz, fold=t.fold)
- # String formatting
- def __repr__(self) -> str:
- us = ""
- if self.microsecond:
- us = f", {self.microsecond}"
- tzinfo = ""
- if self.tzinfo:
- tzinfo = f", tzinfo={self.tzinfo!r}"
- return (
- f"{self.__class__.__name__}"
- f"({self.hour}, {self.minute}, {self.second}{us}{tzinfo})"
- )
- # Comparisons
- def closest(self, dt1: Time | time, dt2: Time | time) -> Self:
- """
- Get the closest time from the instance.
- """
- dt1 = self.__class__(dt1.hour, dt1.minute, dt1.second, dt1.microsecond)
- dt2 = self.__class__(dt2.hour, dt2.minute, dt2.second, dt2.microsecond)
- if self.diff(dt1).in_seconds() < self.diff(dt2).in_seconds():
- return dt1
- return dt2
- def farthest(self, dt1: Time | time, dt2: Time | time) -> Self:
- """
- Get the farthest time from the instance.
- """
- dt1 = self.__class__(dt1.hour, dt1.minute, dt1.second, dt1.microsecond)
- dt2 = self.__class__(dt2.hour, dt2.minute, dt2.second, dt2.microsecond)
- if self.diff(dt1).in_seconds() > self.diff(dt2).in_seconds():
- return dt1
- return dt2
- # ADDITIONS AND SUBSTRACTIONS
- def add(
- self, hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0
- ) -> Time:
- """
- Add duration to the instance.
- :param hours: The number of hours
- :param minutes: The number of minutes
- :param seconds: The number of seconds
- :param microseconds: The number of microseconds
- """
- from pendulum.datetime import DateTime
- return (
- DateTime.EPOCH.at(self.hour, self.minute, self.second, self.microsecond)
- .add(
- hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds
- )
- .time()
- )
- def subtract(
- self, hours: int = 0, minutes: int = 0, seconds: int = 0, microseconds: int = 0
- ) -> Time:
- """
- Add duration to the instance.
- :param hours: The number of hours
- :type hours: int
- :param minutes: The number of minutes
- :type minutes: int
- :param seconds: The number of seconds
- :type seconds: int
- :param microseconds: The number of microseconds
- :type microseconds: int
- :rtype: Time
- """
- from pendulum.datetime import DateTime
- return (
- DateTime.EPOCH.at(self.hour, self.minute, self.second, self.microsecond)
- .subtract(
- hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds
- )
- .time()
- )
- def add_timedelta(self, delta: datetime.timedelta) -> Time:
- """
- Add timedelta duration to the instance.
- :param delta: The timedelta instance
- """
- if delta.days:
- raise TypeError("Cannot add timedelta with days to Time.")
- return self.add(seconds=delta.seconds, microseconds=delta.microseconds)
- def subtract_timedelta(self, delta: datetime.timedelta) -> Time:
- """
- Remove timedelta duration from the instance.
- :param delta: The timedelta instance
- """
- if delta.days:
- raise TypeError("Cannot subtract timedelta with days to Time.")
- return self.subtract(seconds=delta.seconds, microseconds=delta.microseconds)
- def __add__(self, other: datetime.timedelta) -> Time:
- if not isinstance(other, timedelta):
- return NotImplemented
- return self.add_timedelta(other)
- @overload
- def __sub__(self, other: time) -> pendulum.Duration:
- ...
- @overload
- def __sub__(self, other: datetime.timedelta) -> Time:
- ...
- def __sub__(self, other: time | datetime.timedelta) -> pendulum.Duration | Time:
- if not isinstance(other, (Time, time, timedelta)):
- return NotImplemented
- if isinstance(other, timedelta):
- return self.subtract_timedelta(other)
- if isinstance(other, time):
- if other.tzinfo is not None:
- raise TypeError("Cannot subtract aware times to or from Time.")
- other = self.__class__(
- other.hour, other.minute, other.second, other.microsecond
- )
- return other.diff(self, False)
- @overload
- def __rsub__(self, other: time) -> pendulum.Duration:
- ...
- @overload
- def __rsub__(self, other: datetime.timedelta) -> Time:
- ...
- def __rsub__(self, other: time | datetime.timedelta) -> pendulum.Duration | Time:
- if not isinstance(other, (Time, time)):
- return NotImplemented
- if isinstance(other, time):
- if other.tzinfo is not None:
- raise TypeError("Cannot subtract aware times to or from Time.")
- other = self.__class__(
- other.hour, other.minute, other.second, other.microsecond
- )
- return other.__sub__(self)
- # DIFFERENCES
- def diff(self, dt: time | None = None, abs: bool = True) -> Duration:
- """
- Returns the difference between two Time objects as an Duration.
- :param dt: The time to subtract from
- :param abs: Whether to return an absolute duration or not
- """
- if dt is None:
- dt = pendulum.now().time()
- else:
- dt = self.__class__(dt.hour, dt.minute, dt.second, dt.microsecond)
- us1 = (
- self.hour * SECS_PER_HOUR + self.minute * SECS_PER_MIN + self.second
- ) * USECS_PER_SEC
- us2 = (
- dt.hour * SECS_PER_HOUR + dt.minute * SECS_PER_MIN + dt.second
- ) * USECS_PER_SEC
- klass = Duration
- if abs:
- klass = AbsoluteDuration
- return klass(microseconds=us2 - us1)
- def diff_for_humans(
- self,
- other: time | None = None,
- absolute: bool = False,
- locale: str | None = None,
- ) -> str:
- """
- Get the difference in a human readable format in the current locale.
- :param dt: The time to subtract from
- :param absolute: removes time difference modifiers ago, after, etc
- :param locale: The locale to use for localization
- """
- is_now = other is None
- if is_now:
- other = pendulum.now().time()
- diff = self.diff(other)
- return pendulum.format_diff(diff, is_now, absolute, locale)
- # Compatibility methods
- def replace(
- self,
- hour: SupportsIndex | None = None,
- minute: SupportsIndex | None = None,
- second: SupportsIndex | None = None,
- microsecond: SupportsIndex | None = None,
- tzinfo: bool | datetime.tzinfo | Literal[True] | None = True,
- fold: int = 0,
- ) -> Self:
- if tzinfo is True:
- tzinfo = self.tzinfo
- hour = hour if hour is not None else self.hour
- minute = minute if minute is not None else self.minute
- second = second if second is not None else self.second
- microsecond = microsecond if microsecond is not None else self.microsecond
- t = super().replace(
- hour,
- minute,
- second,
- microsecond,
- tzinfo=cast(Optional[datetime.tzinfo], tzinfo),
- fold=fold,
- )
- return self.__class__(
- t.hour, t.minute, t.second, t.microsecond, tzinfo=t.tzinfo
- )
- def __getnewargs__(self) -> tuple[Time]:
- return (self,)
- def _get_state(
- self, protocol: SupportsIndex = 3
- ) -> tuple[int, int, int, int, datetime.tzinfo | None]:
- tz = self.tzinfo
- return self.hour, self.minute, self.second, self.microsecond, tz
- def __reduce__(
- self,
- ) -> tuple[type[Time], tuple[int, int, int, int, datetime.tzinfo | None]]:
- return self.__reduce_ex__(2)
- def __reduce_ex__(
- self, protocol: SupportsIndex
- ) -> tuple[type[Time], tuple[int, int, int, int, datetime.tzinfo | None]]:
- return self.__class__, self._get_state(protocol)
- Time.min = Time(0, 0, 0)
- Time.max = Time(23, 59, 59, 999999)
- Time.resolution = Duration(microseconds=1)
|