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

118 statements  

1from __future__ import annotations 

2 

3import os 

4import struct 

5 

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 

15 

16import pendulum 

17 

18from pendulum.constants import DAYS_PER_MONTHS 

19from pendulum.day import WeekDay 

20from pendulum.locales.locale import Locale 

21 

22 

23if TYPE_CHECKING: 

24 # Prevent import cycles 

25 from pendulum.duration import Duration 

26 

27 # LazyLoaded 

28 from pendulum.formatting.difference_formatter import DifferenceFormatter 

29 

30with_extensions = os.getenv("PENDULUM_EXTENSIONS", "1") == "1" 

31 

32_DT = TypeVar("_DT", bound=datetime) 

33_D = TypeVar("_D", bound=date) 

34 

35try: 

36 if not with_extensions or struct.calcsize("P") == 4: 

37 raise ImportError() 

38 

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 

54 

55difference_formatter: DifferenceFormatter 

56 

57 

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: ... 

70 

71 

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 

81 

82 

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 

98 

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.") 

105 

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 

112 

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 

118 

119 if abs(minutes) > 59: 

120 s = _sign(minutes) 

121 div, mod = divmod(minutes * s, 60) 

122 minutes = mod * s 

123 hours += div * s 

124 

125 if abs(hours) > 23: 

126 s = _sign(hours) 

127 div, mod = divmod(hours * s, 24) 

128 hours = mod * s 

129 days += div * s 

130 

131 if abs(months) > 11: 

132 s = _sign(months) 

133 div, mod = divmod(months * s, 12) 

134 months = mod * s 

135 years += div * s 

136 

137 year = dt.year + years 

138 month = dt.month 

139 

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 

148 

149 day = min(DAYS_PER_MONTHS[int(is_leap(year))][month], dt.day) 

150 

151 dt = dt.replace(year=year, month=month, day=day) 

152 

153 return dt + timedelta( 

154 days=days, 

155 hours=hours, 

156 minutes=minutes, 

157 seconds=seconds, 

158 microseconds=microseconds, 

159 ) 

160 

161 

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() 

170 

171 return _difference_formatter().format(diff, is_now, absolute, locale) 

172 

173 

174def _sign(x: float) -> int: 

175 return int(copysign(1, x)) 

176 

177 

178# Global helpers 

179 

180 

181def locale(name: str) -> Locale: 

182 return Locale.load(name) 

183 

184 

185def set_locale(name: str) -> None: 

186 locale(name) 

187 

188 pendulum._LOCALE = name 

189 

190 

191def get_locale() -> str: 

192 return pendulum._LOCALE 

193 

194 

195def week_starts_at(wday: WeekDay) -> None: 

196 if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY: 

197 raise ValueError("Invalid day of week") 

198 

199 pendulum._WEEK_STARTS_AT = wday 

200 

201 

202def week_ends_at(wday: WeekDay) -> None: 

203 if wday < WeekDay.MONDAY or wday > WeekDay.SUNDAY: 

204 raise ValueError("Invalid day of week") 

205 

206 pendulum._WEEK_ENDS_AT = wday 

207 

208 

209@cache 

210def _difference_formatter() -> DifferenceFormatter: 

211 from pendulum.formatting.difference_formatter import DifferenceFormatter 

212 

213 return DifferenceFormatter() 

214 

215 

216def __getattr__(name: str) -> Any: 

217 if name == "difference_formatter": 

218 return _difference_formatter() 

219 raise AttributeError(name) 

220 

221 

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]