Repository URL to install this package:
Version:
3.0.0 ▾
|
pendulum
/
time.py
|
---|
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)