Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pendulum/helpers.py: 39%

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

108 statements  

1from __future__ import annotations 

2 

3import os 

4import struct 

5 

6from datetime import date 

7from datetime import datetime 

8from datetime import timedelta 

9from math import copysign 

10from typing import TYPE_CHECKING 

11from typing import TypeVar 

12from typing import overload 

13 

14import pendulum 

15 

16from pendulum.constants import DAYS_PER_MONTHS 

17from pendulum.day import WeekDay 

18from pendulum.formatting.difference_formatter import DifferenceFormatter 

19from pendulum.locales.locale import Locale 

20 

21 

22if TYPE_CHECKING: 

23 # Prevent import cycles 

24 from pendulum.duration import Duration 

25 

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

27 

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

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

30 

31try: 

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

33 raise ImportError() 

34 

35 from pendulum._pendulum import PreciseDiff 

36 from pendulum._pendulum import days_in_year 

37 from pendulum._pendulum import is_leap 

38 from pendulum._pendulum import is_long_year 

39 from pendulum._pendulum import local_time 

40 from pendulum._pendulum import precise_diff 

41 from pendulum._pendulum import week_day 

42except ImportError: 

43 from pendulum._helpers import PreciseDiff # type: ignore[assignment] 

44 from pendulum._helpers import days_in_year 

45 from pendulum._helpers import is_leap 

46 from pendulum._helpers import is_long_year 

47 from pendulum._helpers import local_time 

48 from pendulum._helpers import precise_diff # type: ignore[assignment] 

49 from pendulum._helpers import week_day 

50 

51difference_formatter = DifferenceFormatter() 

52 

53 

54@overload 

55def add_duration( 

56 dt: _DT, 

57 years: int = 0, 

58 months: int = 0, 

59 weeks: int = 0, 

60 days: int = 0, 

61 hours: int = 0, 

62 minutes: int = 0, 

63 seconds: float = 0, 

64 microseconds: int = 0, 

65) -> _DT: ... 

66 

67 

68@overload 

69def add_duration( 

70 dt: _D, 

71 years: int = 0, 

72 months: int = 0, 

73 weeks: int = 0, 

74 days: int = 0, 

75) -> _D: 

76 pass 

77 

78 

79def add_duration( 

80 dt: date | datetime, 

81 years: int = 0, 

82 months: int = 0, 

83 weeks: int = 0, 

84 days: int = 0, 

85 hours: int = 0, 

86 minutes: int = 0, 

87 seconds: float = 0, 

88 microseconds: int = 0, 

89) -> date | datetime: 

90 """ 

91 Adds a duration to a date/datetime instance. 

92 """ 

93 days += weeks * 7 

94 

95 if ( 

96 isinstance(dt, date) 

97 and not isinstance(dt, datetime) 

98 and any([hours, minutes, seconds, microseconds]) 

99 ): 

100 raise RuntimeError("Time elements cannot be added to a date instance.") 

101 

102 # Normalizing 

103 if abs(microseconds) > 999999: 

104 s = _sign(microseconds) 

105 div, mod = divmod(microseconds * s, 1000000) 

106 microseconds = mod * s 

107 seconds += div * s 

108 

109 if abs(seconds) > 59: 

110 s = _sign(seconds) 

111 div, mod = divmod(seconds * s, 60) # type: ignore[assignment] 

112 seconds = mod * s 

113 minutes += div * s 

114 

115 if abs(minutes) > 59: 

116 s = _sign(minutes) 

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

118 minutes = mod * s 

119 hours += div * s 

120 

121 if abs(hours) > 23: 

122 s = _sign(hours) 

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

124 hours = mod * s 

125 days += div * s 

126 

127 if abs(months) > 11: 

128 s = _sign(months) 

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

130 months = mod * s 

131 years += div * s 

132 

133 year = dt.year + years 

134 month = dt.month 

135 

136 if months: 

137 month += months 

138 if month > 12: 

139 year += 1 

140 month -= 12 

141 elif month < 1: 

142 year -= 1 

143 month += 12 

144 

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

146 

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

148 

149 return dt + timedelta( 

150 days=days, 

151 hours=hours, 

152 minutes=minutes, 

153 seconds=seconds, 

154 microseconds=microseconds, 

155 ) 

156 

157 

158def format_diff( 

159 diff: Duration, 

160 is_now: bool = True, 

161 absolute: bool = False, 

162 locale: str | None = None, 

163) -> str: 

164 if locale is None: 

165 locale = get_locale() 

166 

167 return difference_formatter.format(diff, is_now, absolute, locale) 

168 

169 

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

171 return int(copysign(1, x)) 

172 

173 

174# Global helpers 

175 

176 

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

178 return Locale.load(name) 

179 

180 

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

182 locale(name) 

183 

184 pendulum._LOCALE = name 

185 

186 

187def get_locale() -> str: 

188 return pendulum._LOCALE 

189 

190 

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

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

193 raise ValueError("Invalid day of week") 

194 

195 pendulum._WEEK_STARTS_AT = wday 

196 

197 

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

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

200 raise ValueError("Invalid day of week") 

201 

202 pendulum._WEEK_ENDS_AT = wday 

203 

204 

205__all__ = [ 

206 "PreciseDiff", 

207 "add_duration", 

208 "days_in_year", 

209 "format_diff", 

210 "get_locale", 

211 "is_leap", 

212 "is_long_year", 

213 "local_time", 

214 "locale", 

215 "precise_diff", 

216 "set_locale", 

217 "week_day", 

218 "week_ends_at", 

219 "week_starts_at", 

220]