Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pendulum/helpers.py: 41%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import os
4import struct
6from datetime import date
7from datetime import datetime
8from datetime import timedelta
9from functools import cache
10from math import copysign
11from typing import TYPE_CHECKING
12from typing import Any
13from typing import TypeVar
14from typing import overload
16import pendulum
18from pendulum.constants import DAYS_PER_MONTHS
19from pendulum.day import WeekDay
20from pendulum.locales.locale import Locale
23if TYPE_CHECKING:
24 # Prevent import cycles
25 from pendulum.duration import Duration
27 # LazyLoaded
28 from pendulum.formatting.difference_formatter import DifferenceFormatter
30with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1"
32_DT = TypeVar("_DT", bound=datetime)
33_D = TypeVar("_D", bound=date)
35try:
36 if not with_extensions or struct.calcsize("P") == 4:
37 raise ImportError()
39 from pendulum._pendulum import PreciseDiff
40 from pendulum._pendulum import days_in_year
41 from pendulum._pendulum import is_leap
42 from pendulum._pendulum import is_long_year
43 from pendulum._pendulum import local_time
44 from pendulum._pendulum import precise_diff
45 from pendulum._pendulum import week_day
46except ImportError:
47 from pendulum._helpers import PreciseDiff # type: ignore[assignment]
48 from pendulum._helpers import days_in_year
49 from pendulum._helpers import is_leap
50 from pendulum._helpers import is_long_year
51 from pendulum._helpers import local_time
52 from pendulum._helpers import precise_diff # type: ignore[assignment]
53 from pendulum._helpers import week_day
55difference_formatter: DifferenceFormatter
58@overload
59def add_duration(
60 dt: _DT,
61 years: int = 0,
62 months: int = 0,
63 weeks: int = 0,
64 days: int = 0,
65 hours: int = 0,
66 minutes: int = 0,
67 seconds: float = 0,
68 microseconds: int = 0,
69) -> _DT: ...
72@overload
73def add_duration(
74 dt: _D,
75 years: int = 0,
76 months: int = 0,
77 weeks: int = 0,
78 days: int = 0,
79) -> _D:
80 pass
83def add_duration(
84 dt: date | datetime,
85 years: int = 0,
86 months: int = 0,
87 weeks: int = 0,
88 days: int = 0,
89 hours: int = 0,
90 minutes: int = 0,
91 seconds: float = 0,
92 microseconds: int = 0,
93) -> date | datetime:
94 """
95 Adds a duration to a date/datetime instance.
96 """
97 days += weeks * 7
99 if (
100 isinstance(dt, date)
101 and not isinstance(dt, datetime)
102 and any([hours, minutes, seconds, microseconds])
103 ):
104 raise RuntimeError("Time elements cannot be added to a date instance.")
106 # Normalizing
107 if abs(microseconds) > 999999:
108 s = _sign(microseconds)
109 div, mod = divmod(microseconds * s, 1000000)
110 microseconds = mod * s
111 seconds += div * s
113 if abs(seconds) > 59:
114 s = _sign(seconds)
115 div, mod = divmod(seconds * s, 60) # type: ignore[assignment]
116 seconds = mod * s
117 minutes += div * s
119 if abs(minutes) > 59:
120 s = _sign(minutes)
121 div, mod = divmod(minutes * s, 60)
122 minutes = mod * s
123 hours += div * s
125 if abs(hours) > 23:
126 s = _sign(hours)
127 div, mod = divmod(hours * s, 24)
128 hours = mod * s
129 days += div * s
131 if abs(months) > 11:
132 s = _sign(months)
133 div, mod = divmod(months * s, 12)
134 months = mod * s
135 years += div * s
137 year = dt.year + years
138 month = dt.month
140 if months:
141 month += months
142 if month > 12:
143 year += 1
144 month -= 12
145 elif month < 1:
146 year -= 1
147 month += 12
149 day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day)
151 dt = dt.replace(year=year, month=month, day=day)
153 return dt + timedelta(
154 days=days,
155 hours=hours,
156 minutes=minutes,
157 seconds=seconds,
158 microseconds=microseconds,
159 )
162def format_diff(
163 diff: Duration,
164 is_now: bool = True,
165 absolute: bool = False,
166 locale: str | None = None,
167) -> str:
168 if locale is None:
169 locale = get_locale()
171 return _difference_formatter().format(diff, is_now, absolute, locale)
174def _sign(x: float) -> int:
175 return int(copysign(1, x))
178# Global helpers
181def locale(name: str) -> Locale:
182 return Locale.load(name)
185def set_locale(name: str) -> None:
186 locale(name)
188 pendulum._LOCALE = name
191def get_locale() -> str:
192 return pendulum._LOCALE
195def week_starts_at(wday: WeekDay) -> None:
196 if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY:
197 raise ValueError("Invalid day of week")
199 pendulum._WEEK_STARTS_AT = wday
202def week_ends_at(wday: WeekDay) -> None:
203 if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY:
204 raise ValueError("Invalid day of week")
206 pendulum._WEEK_ENDS_AT = wday
209@cache
210def _difference_formatter() -> DifferenceFormatter:
211 from pendulum.formatting.difference_formatter import DifferenceFormatter
213 return DifferenceFormatter()
216def __getattr__(name: str) -> Any:
217 if name == "difference_formatter":
218 return _difference_formatter()
219 raise AttributeError(name)
222__all__ = [
223 "PreciseDiff",
224 "add_duration",
225 "days_in_year",
226 "format_diff",
227 "get_locale",
228 "is_leap",
229 "is_long_year",
230 "local_time",
231 "locale",
232 "precise_diff",
233 "set_locale",
234 "week_day",
235 "week_ends_at",
236 "week_starts_at",
237]