summaryrefslogtreecommitdiffstats
path: root/pendulum/helpers.py
diff options
context:
space:
mode:
Diffstat (limited to 'pendulum/helpers.py')
-rw-r--r--pendulum/helpers.py447
1 files changed, 223 insertions, 224 deletions
diff --git a/pendulum/helpers.py b/pendulum/helpers.py
index f149ca5..13b7f22 100644
--- a/pendulum/helpers.py
+++ b/pendulum/helpers.py
@@ -1,224 +1,223 @@
-from __future__ import absolute_import
-
-import os
-import struct
-
-from contextlib import contextmanager
-from datetime import date
-from datetime import datetime
-from datetime import timedelta
-from math import copysign
-from typing import TYPE_CHECKING
-from typing import Iterator
-from typing import Optional
-from typing import TypeVar
-from typing import overload
-
-import pendulum
-
-from .constants import DAYS_PER_MONTHS
-from .formatting.difference_formatter import DifferenceFormatter
-from .locales.locale import Locale
-
-
-if TYPE_CHECKING:
- # Prevent import cycles
- from .period import Period
-
-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 ._extensions._helpers import local_time
- from ._extensions._helpers import precise_diff
- from ._extensions._helpers import is_leap
- from ._extensions._helpers import is_long_year
- from ._extensions._helpers import week_day
- from ._extensions._helpers import days_in_year
- from ._extensions._helpers import timestamp
-except ImportError:
- from ._extensions.helpers import local_time # noqa
- from ._extensions.helpers import precise_diff # noqa
- from ._extensions.helpers import is_leap # noqa
- from ._extensions.helpers import is_long_year # noqa
- from ._extensions.helpers import week_day # noqa
- from ._extensions.helpers import days_in_year # noqa
- from ._extensions.helpers import timestamp # noqa
-
-
-difference_formatter = DifferenceFormatter()
-
-
-@overload
-def add_duration(
- dt, # type: _DT
- years=0, # type: int
- months=0, # type: int
- weeks=0, # type: int
- days=0, # type: int
- hours=0, # type: int
- minutes=0, # type: int
- seconds=0, # type: int
- microseconds=0, # type: int
-): # type: (...) -> _DT
- pass
-
-
-@overload
-def add_duration(
- dt, # type: _D
- years=0, # type: int
- months=0, # type: int
- weeks=0, # type: int
- days=0, # type: int
-): # type: (...) -> _D
- pass
-
-
-def add_duration(
- dt,
- years=0,
- months=0,
- weeks=0,
- days=0,
- hours=0,
- minutes=0,
- seconds=0,
- microseconds=0,
-):
- """
- 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)
- 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, is_now=True, absolute=False, locale=None
-): # type: (Period, bool, bool, Optional[str]) -> str
- if locale is None:
- locale = get_locale()
-
- return difference_formatter.format(diff, is_now, absolute, locale)
-
-
-def _sign(x):
- return int(copysign(1, x))
-
-
-# Global helpers
-
-
-@contextmanager
-def test(mock): # type: (pendulum.DateTime) -> Iterator[None]
- set_test_now(mock)
- try:
- yield
- finally:
- set_test_now()
-
-
-def set_test_now(test_now=None): # type: (Optional[pendulum.DateTime]) -> None
- pendulum._TEST_NOW = test_now
-
-
-def get_test_now(): # type: () -> Optional[pendulum.DateTime]
- return pendulum._TEST_NOW
-
-
-def has_test_now(): # type: () -> bool
- return pendulum._TEST_NOW is not None
-
-
-def locale(name): # type: (str) -> Locale
- return Locale.load(name)
-
-
-def set_locale(name): # type: (str) -> None
- locale(name)
-
- pendulum._LOCALE = name
-
-
-def get_locale(): # type: () -> str
- return pendulum._LOCALE
-
-
-def week_starts_at(wday): # type: (int) -> None
- if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY:
- raise ValueError("Invalid week day as start of week.")
-
- pendulum._WEEK_STARTS_AT = wday
-
-
-def week_ends_at(wday): # type: (int) -> None
- if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY:
- raise ValueError("Invalid week day as start of week.")
-
- pendulum._WEEK_ENDS_AT = wday
+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.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:
+ # nopycln: file # noqa: E800
+ if not with_extensions or struct.calcsize("P") == 4:
+ raise ImportError()
+
+ from pendulum._extensions._helpers import PreciseDiff
+ from pendulum._extensions._helpers import days_in_year
+ from pendulum._extensions._helpers import is_leap
+ from pendulum._extensions._helpers import is_long_year
+ from pendulum._extensions._helpers import local_time
+ from pendulum._extensions._helpers import precise_diff
+ from pendulum._extensions._helpers import timestamp
+ from pendulum._extensions._helpers import week_day
+except ImportError:
+ from pendulum._extensions.helpers import PreciseDiff # type: ignore[misc]
+ from pendulum._extensions.helpers import days_in_year
+ from pendulum._extensions.helpers import is_leap
+ from pendulum._extensions.helpers import is_long_year
+ from pendulum._extensions.helpers import local_time
+ from pendulum._extensions.helpers import precise_diff # type: ignore[misc]
+ from pendulum._extensions.helpers import timestamp
+ from pendulum._extensions.helpers import week_day
+
+difference_formatter = DifferenceFormatter()
+
+
+@overload
+def add_duration(
+ dt: 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,
+) -> datetime:
+ ...
+
+
+@overload
+def add_duration(
+ dt: date,
+ years: int = 0,
+ months: int = 0,
+ weeks: int = 0,
+ days: int = 0,
+) -> date:
+ 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: int) -> None:
+ if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY:
+ raise ValueError("Invalid week day as start of week.")
+
+ pendulum._WEEK_STARTS_AT = wday
+
+
+def week_ends_at(wday: int) -> None:
+ if wday < pendulum.SUNDAY or wday > pendulum.SATURDAY:
+ raise ValueError("Invalid week day as start of week.")
+
+ pendulum._WEEK_ENDS_AT = wday
+
+
+__all__ = [
+ "PreciseDiff",
+ "days_in_year",
+ "is_leap",
+ "is_long_year",
+ "local_time",
+ "precise_diff",
+ "timestamp",
+ "week_day",
+ "add_duration",
+ "format_diff",
+ "locale",
+ "set_locale",
+ "get_locale",
+ "week_starts_at",
+ "week_ends_at",
+]