diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-12-17 14:32:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-12-17 14:32:20 +0000 |
commit | db51f7f103bbbd6c91c8f47d75b3482ef8939691 (patch) | |
tree | ab59b1147bd0cd39f31a48073cff236ede4ec1df /pendulum/duration.py | |
parent | Adding upstream version 3.0.0~a1. (diff) | |
download | pendulum-upstream.tar.xz pendulum-upstream.zip |
Adding upstream version 3.0.0.upstream/3.0.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'pendulum/duration.py')
-rw-r--r-- | pendulum/duration.py | 502 |
1 files changed, 0 insertions, 502 deletions
diff --git a/pendulum/duration.py b/pendulum/duration.py deleted file mode 100644 index a3a68b1..0000000 --- a/pendulum/duration.py +++ /dev/null @@ -1,502 +0,0 @@ -from __future__ import annotations - -from datetime import timedelta -from typing import cast -from typing import overload - -import pendulum - -from pendulum.constants import SECONDS_PER_DAY -from pendulum.constants import SECONDS_PER_HOUR -from pendulum.constants import SECONDS_PER_MINUTE -from pendulum.constants import US_PER_SECOND -from pendulum.utils._compat import PYPY - - -def _divide_and_round(a: float, b: float) -> int: - """divide a by b and round result to the nearest integer - - When the ratio is exactly half-way between two integers, - the even integer is returned. - """ - # Based on the reference implementation for divmod_near - # in Objects/longobject.c. - q, r = divmod(a, b) - - # The output of divmod() is either a float or an int, - # but we always want it to be an int. - q = int(q) - - # round up if either r / b > 0.5, or r / b == 0.5 and q is odd. - # The expression r / b > 0.5 is equivalent to 2 * r > b if b is - # positive, 2 * r < b if b negative. - r *= 2 - greater_than_half = r > b if b > 0 else r < b - if greater_than_half or r == b and q % 2 == 1: - q += 1 - - return q - - -class Duration(timedelta): - """ - Replacement for the standard timedelta class. - - Provides several improvements over the base class. - """ - - _total: float = 0 - _years: int = 0 - _months: int = 0 - _weeks: int = 0 - _days: int = 0 - _remaining_days: int = 0 - _seconds: int = 0 - _microseconds: int = 0 - - _y = None - _m = None - _w = None - _d = None - _h = None - _i = None - _s = None - _invert = None - - def __new__( - cls, - days: float = 0, - seconds: float = 0, - microseconds: float = 0, - milliseconds: float = 0, - minutes: float = 0, - hours: float = 0, - weeks: float = 0, - years: float = 0, - months: float = 0, - ) -> Duration: - if not isinstance(years, int) or not isinstance(months, int): - raise ValueError("Float year and months are not supported") - - self = timedelta.__new__( - cls, - days + years * 365 + months * 30, - seconds, - microseconds, - milliseconds, - minutes, - hours, - weeks, - ) - - # Intuitive normalization - total = self.total_seconds() - (years * 365 + months * 30) * SECONDS_PER_DAY - self._total = total - - m = 1 - if total < 0: - m = -1 - - self._microseconds = round(total % m * 1e6) - self._seconds = abs(int(total)) % SECONDS_PER_DAY * m - - _days = abs(int(total)) // SECONDS_PER_DAY * m - self._days = _days - self._remaining_days = abs(_days) % 7 * m - self._weeks = abs(_days) // 7 * m - self._months = months - self._years = years - - return self - - def total_minutes(self) -> float: - return self.total_seconds() / SECONDS_PER_MINUTE - - def total_hours(self) -> float: - return self.total_seconds() / SECONDS_PER_HOUR - - def total_days(self) -> float: - return self.total_seconds() / SECONDS_PER_DAY - - def total_weeks(self) -> float: - return self.total_days() / 7 - - if PYPY: - - def total_seconds(self) -> float: - days = 0 - - if hasattr(self, "_years"): - days += self._years * 365 - - if hasattr(self, "_months"): - days += self._months * 30 - - if hasattr(self, "_remaining_days"): - days += self._weeks * 7 + self._remaining_days - else: - days += self._days - - return ( - (days * SECONDS_PER_DAY + self._seconds) * US_PER_SECOND - + self._microseconds - ) / US_PER_SECOND - - @property - def years(self) -> int: - return self._years - - @property - def months(self) -> int: - return self._months - - @property - def weeks(self) -> int: - return self._weeks - - if PYPY: - - @property - def days(self) -> int: - return self._years * 365 + self._months * 30 + self._days - - @property - def remaining_days(self) -> int: - return self._remaining_days - - @property - def hours(self) -> int: - if self._h is None: - seconds = self._seconds - self._h = 0 - if abs(seconds) >= 3600: - self._h = (abs(seconds) // 3600 % 24) * self._sign(seconds) - - return self._h - - @property - def minutes(self) -> int: - if self._i is None: - seconds = self._seconds - self._i = 0 - if abs(seconds) >= 60: - self._i = (abs(seconds) // 60 % 60) * self._sign(seconds) - - return self._i - - @property - def seconds(self) -> int: - return self._seconds - - @property - def remaining_seconds(self) -> int: - if self._s is None: - self._s = self._seconds - self._s = abs(self._s) % 60 * self._sign(self._s) - - return self._s - - @property - def microseconds(self) -> int: - return self._microseconds - - @property - def invert(self) -> bool: - if self._invert is None: - self._invert = self.total_seconds() < 0 - - return self._invert - - def in_weeks(self) -> int: - return int(self.total_weeks()) - - def in_days(self) -> int: - return int(self.total_days()) - - def in_hours(self) -> int: - return int(self.total_hours()) - - def in_minutes(self) -> int: - return int(self.total_minutes()) - - def in_seconds(self) -> int: - return int(self.total_seconds()) - - def in_words(self, locale: str | None = None, separator: str = " ") -> str: - """ - Get the current interval in words in the current locale. - - Ex: 6 jours 23 heures 58 minutes - - :param locale: The locale to use. Defaults to current locale. - :param separator: The separator to use between each unit - """ - periods = [ - ("year", self.years), - ("month", self.months), - ("week", self.weeks), - ("day", self.remaining_days), - ("hour", self.hours), - ("minute", self.minutes), - ("second", self.remaining_seconds), - ] - - if locale is None: - locale = pendulum.get_locale() - - loaded_locale = pendulum.locale(locale) - - parts = [] - for period in periods: - unit, period_count = period - if abs(period_count) > 0: - translation = loaded_locale.translation( - f"units.{unit}.{loaded_locale.plural(abs(period_count))}" - ) - parts.append(translation.format(period_count)) - - if not parts: - count: int | str = 0 - if abs(self.microseconds) > 0: - unit = f"units.second.{loaded_locale.plural(1)}" - count = f"{abs(self.microseconds) / 1e6:.2f}" - else: - unit = f"units.microsecond.{loaded_locale.plural(0)}" - translation = loaded_locale.translation(unit) - parts.append(translation.format(count)) - - return separator.join(parts) - - def _sign(self, value: float) -> int: - if value < 0: - return -1 - - return 1 - - def as_timedelta(self) -> timedelta: - """ - Return the interval as a native timedelta. - """ - return timedelta(seconds=self.total_seconds()) - - def __str__(self) -> str: - return self.in_words() - - def __repr__(self) -> str: - rep = f"{self.__class__.__name__}(" - - if self._years: - rep += f"years={self._years}, " - - if self._months: - rep += f"months={self._months}, " - - if self._weeks: - rep += f"weeks={self._weeks}, " - - if self._days: - rep += f"days={self._remaining_days}, " - - if self.hours: - rep += f"hours={self.hours}, " - - if self.minutes: - rep += f"minutes={self.minutes}, " - - if self.remaining_seconds: - rep += f"seconds={self.remaining_seconds}, " - - if self.microseconds: - rep += f"microseconds={self.microseconds}, " - - rep += ")" - - return rep.replace(", )", ")") - - def __add__(self, other: timedelta) -> Duration: - if isinstance(other, timedelta): - return self.__class__(seconds=self.total_seconds() + other.total_seconds()) - - return NotImplemented - - __radd__ = __add__ - - def __sub__(self, other: timedelta) -> Duration: - if isinstance(other, timedelta): - return self.__class__(seconds=self.total_seconds() - other.total_seconds()) - - return NotImplemented - - def __neg__(self) -> Duration: - return self.__class__( - years=-self._years, - months=-self._months, - weeks=-self._weeks, - days=-self._remaining_days, - seconds=-self._seconds, - microseconds=-self._microseconds, - ) - - def _to_microseconds(self) -> int: - return (self._days * (24 * 3600) + self._seconds) * 1000000 + self._microseconds - - def __mul__(self, other: int | float) -> Duration: - if isinstance(other, int): - return self.__class__( - years=self._years * other, - months=self._months * other, - seconds=self._total * other, - ) - - if isinstance(other, float): - usec = self._to_microseconds() - a, b = other.as_integer_ratio() - - return self.__class__(0, 0, _divide_and_round(usec * a, b)) - - return NotImplemented - - __rmul__ = __mul__ - - @overload - def __floordiv__(self, other: timedelta) -> int: - ... - - @overload - def __floordiv__(self, other: int) -> Duration: - ... - - def __floordiv__(self, other: int | timedelta) -> int | Duration: - if not isinstance(other, (int, timedelta)): - return NotImplemented - - usec = self._to_microseconds() - if isinstance(other, timedelta): - return cast(int, usec // other._to_microseconds()) # type: ignore[attr-defined] - - if isinstance(other, int): - return self.__class__( - 0, - 0, - usec // other, - years=self._years // other, - months=self._months // other, - ) - - @overload - def __truediv__(self, other: timedelta) -> float: - ... - - @overload - def __truediv__(self, other: float) -> Duration: - ... - - def __truediv__(self, other: int | float | timedelta) -> Duration | float: - if not isinstance(other, (int, float, timedelta)): - return NotImplemented - - usec = self._to_microseconds() - if isinstance(other, timedelta): - return cast(float, usec / other._to_microseconds()) # type: ignore[attr-defined] - - if isinstance(other, int): - return self.__class__( - 0, - 0, - _divide_and_round(usec, other), - years=_divide_and_round(self._years, other), - months=_divide_and_round(self._months, other), - ) - - if isinstance(other, float): - a, b = other.as_integer_ratio() - - return self.__class__( - 0, - 0, - _divide_and_round(b * usec, a), - years=_divide_and_round(self._years * b, a), - months=_divide_and_round(self._months, other), - ) - - __div__ = __floordiv__ - - def __mod__(self, other: timedelta) -> Duration: - if isinstance(other, timedelta): - r = self._to_microseconds() % other._to_microseconds() # type: ignore[attr-defined] - - return self.__class__(0, 0, r) - - return NotImplemented - - def __divmod__(self, other: timedelta) -> tuple[int, Duration]: - if isinstance(other, timedelta): - q, r = divmod(self._to_microseconds(), other._to_microseconds()) # type: ignore[attr-defined] - - return q, self.__class__(0, 0, r) - - return NotImplemented - - -Duration.min = Duration(days=-999999999) -Duration.max = Duration( - days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999 -) -Duration.resolution = Duration(microseconds=1) - - -class AbsoluteDuration(Duration): - """ - Duration that expresses a time difference in absolute values. - """ - - def __new__( - cls, - days: float = 0, - seconds: float = 0, - microseconds: float = 0, - milliseconds: float = 0, - minutes: float = 0, - hours: float = 0, - weeks: float = 0, - years: float = 0, - months: float = 0, - ) -> AbsoluteDuration: - if not isinstance(years, int) or not isinstance(months, int): - raise ValueError("Float year and months are not supported") - - self = timedelta.__new__( - cls, days, seconds, microseconds, milliseconds, minutes, hours, weeks - ) - - # We need to compute the total_seconds() value - # on a native timedelta object - delta = timedelta( - days, seconds, microseconds, milliseconds, minutes, hours, weeks - ) - - # Intuitive normalization - self._total = delta.total_seconds() - total = abs(self._total) - - self._microseconds = round(total % 1 * 1e6) - self._seconds = int(total) % SECONDS_PER_DAY - - days = int(total) // SECONDS_PER_DAY - self._days = abs(days + years * 365 + months * 30) - self._remaining_days = days % 7 - self._weeks = days // 7 - self._months = abs(months) - self._years = abs(years) - - return self - - def total_seconds(self) -> float: - return abs(self._total) - - @property - def invert(self) -> bool: - if self._invert is None: - self._invert = self._total < 0 - - return self._invert |