123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- from __future__ import annotations
- import os
- import struct
- from datetime import date
- from datetime import datetime
- from datetime import timedelta
- from math import copysign
- from typing import TYPE_CHECKING
- from typing import TypeVar
- from typing import overload
- import pendulum
- from pendulum.constants import DAYS_PER_MONTHS
- from pendulum.day import WeekDay
- from pendulum.formatting.difference_formatter import DifferenceFormatter
- from pendulum.locales.locale import Locale
- if TYPE_CHECKING:
- # Prevent import cycles
- from pendulum.duration import Duration
- with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1"
- _DT = TypeVar("_DT", bound=datetime)
- _D = TypeVar("_D", bound=date)
- try:
- if not with_extensions or struct.calcsize("P") == 4:
- raise ImportError()
- from pendulum._pendulum import PreciseDiff
- from pendulum._pendulum import days_in_year
- from pendulum._pendulum import is_leap
- from pendulum._pendulum import is_long_year
- from pendulum._pendulum import local_time
- from pendulum._pendulum import precise_diff
- from pendulum._pendulum import week_day
- except ImportError:
- from pendulum._helpers import PreciseDiff # type: ignore[assignment]
- from pendulum._helpers import days_in_year
- from pendulum._helpers import is_leap
- from pendulum._helpers import is_long_year
- from pendulum._helpers import local_time
- from pendulum._helpers import precise_diff # type: ignore[assignment]
- from pendulum._helpers import week_day
- difference_formatter = DifferenceFormatter()
- @overload
- def add_duration(
- dt: _DT,
- years: int = 0,
- months: int = 0,
- weeks: int = 0,
- days: int = 0,
- hours: int = 0,
- minutes: int = 0,
- seconds: float = 0,
- microseconds: int = 0,
- ) -> _DT:
- ...
- @overload
- def add_duration(
- dt: _D,
- years: int = 0,
- months: int = 0,
- weeks: int = 0,
- days: int = 0,
- ) -> _D:
- pass
- def add_duration(
- dt: date | datetime,
- years: int = 0,
- months: int = 0,
- weeks: int = 0,
- days: int = 0,
- hours: int = 0,
- minutes: int = 0,
- seconds: float = 0,
- microseconds: int = 0,
- ) -> date | datetime:
- """
- Adds a duration to a date/datetime instance.
- """
- days += weeks * 7
- if (
- isinstance(dt, date)
- and not isinstance(dt, datetime)
- and any([hours, minutes, seconds, microseconds])
- ):
- raise RuntimeError("Time elements cannot be added to a date instance.")
- # Normalizing
- if abs(microseconds) > 999999:
- s = _sign(microseconds)
- div, mod = divmod(microseconds * s, 1000000)
- microseconds = mod * s
- seconds += div * s
- if abs(seconds) > 59:
- s = _sign(seconds)
- div, mod = divmod(seconds * s, 60) # type: ignore[assignment]
- seconds = mod * s
- minutes += div * s
- if abs(minutes) > 59:
- s = _sign(minutes)
- div, mod = divmod(minutes * s, 60)
- minutes = mod * s
- hours += div * s
- if abs(hours) > 23:
- s = _sign(hours)
- div, mod = divmod(hours * s, 24)
- hours = mod * s
- days += div * s
- if abs(months) > 11:
- s = _sign(months)
- div, mod = divmod(months * s, 12)
- months = mod * s
- years += div * s
- year = dt.year + years
- month = dt.month
- if months:
- month += months
- if month > 12:
- year += 1
- month -= 12
- elif month < 1:
- year -= 1
- month += 12
- day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day)
- dt = dt.replace(year=year, month=month, day=day)
- return dt + timedelta(
- days=days,
- hours=hours,
- minutes=minutes,
- seconds=seconds,
- microseconds=microseconds,
- )
- def format_diff(
- diff: Duration,
- is_now: bool = True,
- absolute: bool = False,
- locale: str | None = None,
- ) -> str:
- if locale is None:
- locale = get_locale()
- return difference_formatter.format(diff, is_now, absolute, locale)
- def _sign(x: float) -> int:
- return int(copysign(1, x))
- # Global helpers
- def locale(name: str) -> Locale:
- return Locale.load(name)
- def set_locale(name: str) -> None:
- locale(name)
- pendulum._LOCALE = name
- def get_locale() -> str:
- return pendulum._LOCALE
- def week_starts_at(wday: WeekDay) -> None:
- if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY:
- raise ValueError("Invalid day of week")
- pendulum._WEEK_STARTS_AT = wday
- def week_ends_at(wday: WeekDay) -> None:
- if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY:
- raise ValueError("Invalid day of week")
- pendulum._WEEK_ENDS_AT = wday
- __all__ = [
- "PreciseDiff",
- "days_in_year",
- "is_leap",
- "is_long_year",
- "local_time",
- "precise_diff",
- "week_day",
- "add_duration",
- "format_diff",
- "locale",
- "set_locale",
- "get_locale",
- "week_starts_at",
- "week_ends_at",
- ]
|