Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pendulum/period.py: 32%
190 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1from __future__ import absolute_import
3import operator
5from datetime import date
6from datetime import datetime
7from datetime import timedelta
9import pendulum
11from pendulum.utils._compat import _HAS_FOLD
12from pendulum.utils._compat import decode
14from .constants import MONTHS_PER_YEAR
15from .duration import Duration
16from .helpers import precise_diff
19class Period(Duration):
20 """
21 Duration class that is aware of the datetimes that generated the
22 time difference.
23 """
25 def __new__(cls, start, end, absolute=False):
26 if isinstance(start, datetime) and isinstance(end, datetime):
27 if (
28 start.tzinfo is None
29 and end.tzinfo is not None
30 or start.tzinfo is not None
31 and end.tzinfo is None
32 ):
33 raise TypeError("can't compare offset-naive and offset-aware datetimes")
35 if absolute and start > end:
36 end, start = start, end
38 _start = start
39 _end = end
40 if isinstance(start, pendulum.DateTime):
41 if _HAS_FOLD:
42 _start = datetime(
43 start.year,
44 start.month,
45 start.day,
46 start.hour,
47 start.minute,
48 start.second,
49 start.microsecond,
50 tzinfo=start.tzinfo,
51 fold=start.fold,
52 )
53 else:
54 _start = datetime(
55 start.year,
56 start.month,
57 start.day,
58 start.hour,
59 start.minute,
60 start.second,
61 start.microsecond,
62 tzinfo=start.tzinfo,
63 )
64 elif isinstance(start, pendulum.Date):
65 _start = date(start.year, start.month, start.day)
67 if isinstance(end, pendulum.DateTime):
68 if _HAS_FOLD:
69 _end = datetime(
70 end.year,
71 end.month,
72 end.day,
73 end.hour,
74 end.minute,
75 end.second,
76 end.microsecond,
77 tzinfo=end.tzinfo,
78 fold=end.fold,
79 )
80 else:
81 _end = datetime(
82 end.year,
83 end.month,
84 end.day,
85 end.hour,
86 end.minute,
87 end.second,
88 end.microsecond,
89 tzinfo=end.tzinfo,
90 )
91 elif isinstance(end, pendulum.Date):
92 _end = date(end.year, end.month, end.day)
94 # Fixing issues with datetime.__sub__()
95 # not handling offsets if the tzinfo is the same
96 if (
97 isinstance(_start, datetime)
98 and isinstance(_end, datetime)
99 and _start.tzinfo is _end.tzinfo
100 ):
101 if _start.tzinfo is not None:
102 _start = (_start - start.utcoffset()).replace(tzinfo=None)
104 if isinstance(end, datetime) and _end.tzinfo is not None:
105 _end = (_end - end.utcoffset()).replace(tzinfo=None)
107 delta = _end - _start
109 return super(Period, cls).__new__(cls, seconds=delta.total_seconds())
111 def __init__(self, start, end, absolute=False):
112 super(Period, self).__init__()
114 if not isinstance(start, pendulum.Date):
115 if isinstance(start, datetime):
116 start = pendulum.instance(start)
117 else:
118 start = pendulum.date(start.year, start.month, start.day)
120 _start = start
121 else:
122 if isinstance(start, pendulum.DateTime):
123 _start = datetime(
124 start.year,
125 start.month,
126 start.day,
127 start.hour,
128 start.minute,
129 start.second,
130 start.microsecond,
131 tzinfo=start.tzinfo,
132 )
133 else:
134 _start = date(start.year, start.month, start.day)
136 if not isinstance(end, pendulum.Date):
137 if isinstance(end, datetime):
138 end = pendulum.instance(end)
139 else:
140 end = pendulum.date(end.year, end.month, end.day)
142 _end = end
143 else:
144 if isinstance(end, pendulum.DateTime):
145 _end = datetime(
146 end.year,
147 end.month,
148 end.day,
149 end.hour,
150 end.minute,
151 end.second,
152 end.microsecond,
153 tzinfo=end.tzinfo,
154 )
155 else:
156 _end = date(end.year, end.month, end.day)
158 self._invert = False
159 if start > end:
160 self._invert = True
162 if absolute:
163 end, start = start, end
164 _end, _start = _start, _end
166 self._absolute = absolute
167 self._start = start
168 self._end = end
169 self._delta = precise_diff(_start, _end)
171 @property
172 def years(self):
173 return self._delta.years
175 @property
176 def months(self):
177 return self._delta.months
179 @property
180 def weeks(self):
181 return abs(self._delta.days) // 7 * self._sign(self._delta.days)
183 @property
184 def days(self):
185 return self._days
187 @property
188 def remaining_days(self):
189 return abs(self._delta.days) % 7 * self._sign(self._days)
191 @property
192 def hours(self):
193 return self._delta.hours
195 @property
196 def minutes(self):
197 return self._delta.minutes
199 @property
200 def start(self):
201 return self._start
203 @property
204 def end(self):
205 return self._end
207 def in_years(self):
208 """
209 Gives the duration of the Period in full years.
211 :rtype: int
212 """
213 return self.years
215 def in_months(self):
216 """
217 Gives the duration of the Period in full months.
219 :rtype: int
220 """
221 return self.years * MONTHS_PER_YEAR + self.months
223 def in_weeks(self):
224 days = self.in_days()
225 sign = 1
227 if days < 0:
228 sign = -1
230 return sign * (abs(days) // 7)
232 def in_days(self):
233 return self._delta.total_days
235 def in_words(self, locale=None, separator=" "):
236 """
237 Get the current interval in words in the current locale.
239 Ex: 6 jours 23 heures 58 minutes
241 :param locale: The locale to use. Defaults to current locale.
242 :type locale: str
244 :param separator: The separator to use between each unit
245 :type separator: str
247 :rtype: str
248 """
249 periods = [
250 ("year", self.years),
251 ("month", self.months),
252 ("week", self.weeks),
253 ("day", self.remaining_days),
254 ("hour", self.hours),
255 ("minute", self.minutes),
256 ("second", self.remaining_seconds),
257 ]
259 if locale is None:
260 locale = pendulum.get_locale()
262 locale = pendulum.locale(locale)
263 parts = []
264 for period in periods:
265 unit, count = period
266 if abs(count) > 0:
267 translation = locale.translation(
268 "units.{}.{}".format(unit, locale.plural(abs(count)))
269 )
270 parts.append(translation.format(count))
272 if not parts:
273 if abs(self.microseconds) > 0:
274 unit = "units.second.{}".format(locale.plural(1))
275 count = "{:.2f}".format(abs(self.microseconds) / 1e6)
276 else:
277 unit = "units.microsecond.{}".format(locale.plural(0))
278 count = 0
279 translation = locale.translation(unit)
280 parts.append(translation.format(count))
282 return decode(separator.join(parts))
284 def range(self, unit, amount=1):
285 method = "add"
286 op = operator.le
287 if not self._absolute and self.invert:
288 method = "subtract"
289 op = operator.ge
291 start, end = self.start, self.end
293 i = amount
294 while op(start, end):
295 yield start
297 start = getattr(self.start, method)(**{unit: i})
299 i += amount
301 def as_interval(self):
302 """
303 Return the Period as an Duration.
305 :rtype: Duration
306 """
307 return Duration(seconds=self.total_seconds())
309 def __iter__(self):
310 return self.range("days")
312 def __contains__(self, item):
313 return self.start <= item <= self.end
315 def __add__(self, other):
316 return self.as_interval().__add__(other)
318 __radd__ = __add__
320 def __sub__(self, other):
321 return self.as_interval().__sub__(other)
323 def __neg__(self):
324 return self.__class__(self.end, self.start, self._absolute)
326 def __mul__(self, other):
327 return self.as_interval().__mul__(other)
329 __rmul__ = __mul__
331 def __floordiv__(self, other):
332 return self.as_interval().__floordiv__(other)
334 def __truediv__(self, other):
335 return self.as_interval().__truediv__(other)
337 __div__ = __floordiv__
339 def __mod__(self, other):
340 return self.as_interval().__mod__(other)
342 def __divmod__(self, other):
343 return self.as_interval().__divmod__(other)
345 def __abs__(self):
346 return self.__class__(self.start, self.end, True)
348 def __repr__(self):
349 return "<Period [{} -> {}]>".format(self._start, self._end)
351 def __str__(self):
352 return self.__repr__()
354 def _cmp(self, other):
355 # Only needed for PyPy
356 assert isinstance(other, timedelta)
358 if isinstance(other, Period):
359 other = other.as_timedelta()
361 td = self.as_timedelta()
363 return 0 if td == other else 1 if td > other else -1
365 def _getstate(self, protocol=3):
366 start, end = self.start, self.end
368 if self._invert and self._absolute:
369 end, start = start, end
371 return (start, end, self._absolute)
373 def __reduce__(self):
374 return self.__reduce_ex__(2)
376 def __reduce_ex__(self, protocol):
377 return self.__class__, self._getstate(protocol)
379 def __hash__(self):
380 return hash((self.start, self.end, self._absolute))
382 def __eq__(self, other):
383 if isinstance(other, Period):
384 return (self.start, self.end, self._absolute) == (
385 other.start,
386 other.end,
387 other._absolute,
388 )
389 else:
390 return self.as_interval() == other