Mercurial > repos > shellac > guppy_basecaller
comparison env/lib/python3.7/site-packages/dateutil/tz/_common.py @ 5:9b1c78e6ba9c draft default tip
"planemo upload commit 6c0a8142489327ece472c84e558c47da711a9142"
author | shellac |
---|---|
date | Mon, 01 Jun 2020 08:59:25 -0400 |
parents | 79f47841a781 |
children |
comparison
equal
deleted
inserted
replaced
4:79f47841a781 | 5:9b1c78e6ba9c |
---|---|
1 from six import PY2 | |
2 | |
3 from functools import wraps | |
4 | |
5 from datetime import datetime, timedelta, tzinfo | |
6 | |
7 | |
8 ZERO = timedelta(0) | |
9 | |
10 __all__ = ['tzname_in_python2', 'enfold'] | |
11 | |
12 | |
13 def tzname_in_python2(namefunc): | |
14 """Change unicode output into bytestrings in Python 2 | |
15 | |
16 tzname() API changed in Python 3. It used to return bytes, but was changed | |
17 to unicode strings | |
18 """ | |
19 if PY2: | |
20 @wraps(namefunc) | |
21 def adjust_encoding(*args, **kwargs): | |
22 name = namefunc(*args, **kwargs) | |
23 if name is not None: | |
24 name = name.encode() | |
25 | |
26 return name | |
27 | |
28 return adjust_encoding | |
29 else: | |
30 return namefunc | |
31 | |
32 | |
33 # The following is adapted from Alexander Belopolsky's tz library | |
34 # https://github.com/abalkin/tz | |
35 if hasattr(datetime, 'fold'): | |
36 # This is the pre-python 3.6 fold situation | |
37 def enfold(dt, fold=1): | |
38 """ | |
39 Provides a unified interface for assigning the ``fold`` attribute to | |
40 datetimes both before and after the implementation of PEP-495. | |
41 | |
42 :param fold: | |
43 The value for the ``fold`` attribute in the returned datetime. This | |
44 should be either 0 or 1. | |
45 | |
46 :return: | |
47 Returns an object for which ``getattr(dt, 'fold', 0)`` returns | |
48 ``fold`` for all versions of Python. In versions prior to | |
49 Python 3.6, this is a ``_DatetimeWithFold`` object, which is a | |
50 subclass of :py:class:`datetime.datetime` with the ``fold`` | |
51 attribute added, if ``fold`` is 1. | |
52 | |
53 .. versionadded:: 2.6.0 | |
54 """ | |
55 return dt.replace(fold=fold) | |
56 | |
57 else: | |
58 class _DatetimeWithFold(datetime): | |
59 """ | |
60 This is a class designed to provide a PEP 495-compliant interface for | |
61 Python versions before 3.6. It is used only for dates in a fold, so | |
62 the ``fold`` attribute is fixed at ``1``. | |
63 | |
64 .. versionadded:: 2.6.0 | |
65 """ | |
66 __slots__ = () | |
67 | |
68 def replace(self, *args, **kwargs): | |
69 """ | |
70 Return a datetime with the same attributes, except for those | |
71 attributes given new values by whichever keyword arguments are | |
72 specified. Note that tzinfo=None can be specified to create a naive | |
73 datetime from an aware datetime with no conversion of date and time | |
74 data. | |
75 | |
76 This is reimplemented in ``_DatetimeWithFold`` because pypy3 will | |
77 return a ``datetime.datetime`` even if ``fold`` is unchanged. | |
78 """ | |
79 argnames = ( | |
80 'year', 'month', 'day', 'hour', 'minute', 'second', | |
81 'microsecond', 'tzinfo' | |
82 ) | |
83 | |
84 for arg, argname in zip(args, argnames): | |
85 if argname in kwargs: | |
86 raise TypeError('Duplicate argument: {}'.format(argname)) | |
87 | |
88 kwargs[argname] = arg | |
89 | |
90 for argname in argnames: | |
91 if argname not in kwargs: | |
92 kwargs[argname] = getattr(self, argname) | |
93 | |
94 dt_class = self.__class__ if kwargs.get('fold', 1) else datetime | |
95 | |
96 return dt_class(**kwargs) | |
97 | |
98 @property | |
99 def fold(self): | |
100 return 1 | |
101 | |
102 def enfold(dt, fold=1): | |
103 """ | |
104 Provides a unified interface for assigning the ``fold`` attribute to | |
105 datetimes both before and after the implementation of PEP-495. | |
106 | |
107 :param fold: | |
108 The value for the ``fold`` attribute in the returned datetime. This | |
109 should be either 0 or 1. | |
110 | |
111 :return: | |
112 Returns an object for which ``getattr(dt, 'fold', 0)`` returns | |
113 ``fold`` for all versions of Python. In versions prior to | |
114 Python 3.6, this is a ``_DatetimeWithFold`` object, which is a | |
115 subclass of :py:class:`datetime.datetime` with the ``fold`` | |
116 attribute added, if ``fold`` is 1. | |
117 | |
118 .. versionadded:: 2.6.0 | |
119 """ | |
120 if getattr(dt, 'fold', 0) == fold: | |
121 return dt | |
122 | |
123 args = dt.timetuple()[:6] | |
124 args += (dt.microsecond, dt.tzinfo) | |
125 | |
126 if fold: | |
127 return _DatetimeWithFold(*args) | |
128 else: | |
129 return datetime(*args) | |
130 | |
131 | |
132 def _validate_fromutc_inputs(f): | |
133 """ | |
134 The CPython version of ``fromutc`` checks that the input is a ``datetime`` | |
135 object and that ``self`` is attached as its ``tzinfo``. | |
136 """ | |
137 @wraps(f) | |
138 def fromutc(self, dt): | |
139 if not isinstance(dt, datetime): | |
140 raise TypeError("fromutc() requires a datetime argument") | |
141 if dt.tzinfo is not self: | |
142 raise ValueError("dt.tzinfo is not self") | |
143 | |
144 return f(self, dt) | |
145 | |
146 return fromutc | |
147 | |
148 | |
149 class _tzinfo(tzinfo): | |
150 """ | |
151 Base class for all ``dateutil`` ``tzinfo`` objects. | |
152 """ | |
153 | |
154 def is_ambiguous(self, dt): | |
155 """ | |
156 Whether or not the "wall time" of a given datetime is ambiguous in this | |
157 zone. | |
158 | |
159 :param dt: | |
160 A :py:class:`datetime.datetime`, naive or time zone aware. | |
161 | |
162 | |
163 :return: | |
164 Returns ``True`` if ambiguous, ``False`` otherwise. | |
165 | |
166 .. versionadded:: 2.6.0 | |
167 """ | |
168 | |
169 dt = dt.replace(tzinfo=self) | |
170 | |
171 wall_0 = enfold(dt, fold=0) | |
172 wall_1 = enfold(dt, fold=1) | |
173 | |
174 same_offset = wall_0.utcoffset() == wall_1.utcoffset() | |
175 same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None) | |
176 | |
177 return same_dt and not same_offset | |
178 | |
179 def _fold_status(self, dt_utc, dt_wall): | |
180 """ | |
181 Determine the fold status of a "wall" datetime, given a representation | |
182 of the same datetime as a (naive) UTC datetime. This is calculated based | |
183 on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all | |
184 datetimes, and that this offset is the actual number of hours separating | |
185 ``dt_utc`` and ``dt_wall``. | |
186 | |
187 :param dt_utc: | |
188 Representation of the datetime as UTC | |
189 | |
190 :param dt_wall: | |
191 Representation of the datetime as "wall time". This parameter must | |
192 either have a `fold` attribute or have a fold-naive | |
193 :class:`datetime.tzinfo` attached, otherwise the calculation may | |
194 fail. | |
195 """ | |
196 if self.is_ambiguous(dt_wall): | |
197 delta_wall = dt_wall - dt_utc | |
198 _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst())) | |
199 else: | |
200 _fold = 0 | |
201 | |
202 return _fold | |
203 | |
204 def _fold(self, dt): | |
205 return getattr(dt, 'fold', 0) | |
206 | |
207 def _fromutc(self, dt): | |
208 """ | |
209 Given a timezone-aware datetime in a given timezone, calculates a | |
210 timezone-aware datetime in a new timezone. | |
211 | |
212 Since this is the one time that we *know* we have an unambiguous | |
213 datetime object, we take this opportunity to determine whether the | |
214 datetime is ambiguous and in a "fold" state (e.g. if it's the first | |
215 occurrence, chronologically, of the ambiguous datetime). | |
216 | |
217 :param dt: | |
218 A timezone-aware :class:`datetime.datetime` object. | |
219 """ | |
220 | |
221 # Re-implement the algorithm from Python's datetime.py | |
222 dtoff = dt.utcoffset() | |
223 if dtoff is None: | |
224 raise ValueError("fromutc() requires a non-None utcoffset() " | |
225 "result") | |
226 | |
227 # The original datetime.py code assumes that `dst()` defaults to | |
228 # zero during ambiguous times. PEP 495 inverts this presumption, so | |
229 # for pre-PEP 495 versions of python, we need to tweak the algorithm. | |
230 dtdst = dt.dst() | |
231 if dtdst is None: | |
232 raise ValueError("fromutc() requires a non-None dst() result") | |
233 delta = dtoff - dtdst | |
234 | |
235 dt += delta | |
236 # Set fold=1 so we can default to being in the fold for | |
237 # ambiguous dates. | |
238 dtdst = enfold(dt, fold=1).dst() | |
239 if dtdst is None: | |
240 raise ValueError("fromutc(): dt.dst gave inconsistent " | |
241 "results; cannot convert") | |
242 return dt + dtdst | |
243 | |
244 @_validate_fromutc_inputs | |
245 def fromutc(self, dt): | |
246 """ | |
247 Given a timezone-aware datetime in a given timezone, calculates a | |
248 timezone-aware datetime in a new timezone. | |
249 | |
250 Since this is the one time that we *know* we have an unambiguous | |
251 datetime object, we take this opportunity to determine whether the | |
252 datetime is ambiguous and in a "fold" state (e.g. if it's the first | |
253 occurrence, chronologically, of the ambiguous datetime). | |
254 | |
255 :param dt: | |
256 A timezone-aware :class:`datetime.datetime` object. | |
257 """ | |
258 dt_wall = self._fromutc(dt) | |
259 | |
260 # Calculate the fold status given the two datetimes. | |
261 _fold = self._fold_status(dt, dt_wall) | |
262 | |
263 # Set the default fold value for ambiguous dates | |
264 return enfold(dt_wall, fold=_fold) | |
265 | |
266 | |
267 class tzrangebase(_tzinfo): | |
268 """ | |
269 This is an abstract base class for time zones represented by an annual | |
270 transition into and out of DST. Child classes should implement the following | |
271 methods: | |
272 | |
273 * ``__init__(self, *args, **kwargs)`` | |
274 * ``transitions(self, year)`` - this is expected to return a tuple of | |
275 datetimes representing the DST on and off transitions in standard | |
276 time. | |
277 | |
278 A fully initialized ``tzrangebase`` subclass should also provide the | |
279 following attributes: | |
280 * ``hasdst``: Boolean whether or not the zone uses DST. | |
281 * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects | |
282 representing the respective UTC offsets. | |
283 * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short | |
284 abbreviations in DST and STD, respectively. | |
285 * ``_hasdst``: Whether or not the zone has DST. | |
286 | |
287 .. versionadded:: 2.6.0 | |
288 """ | |
289 def __init__(self): | |
290 raise NotImplementedError('tzrangebase is an abstract base class') | |
291 | |
292 def utcoffset(self, dt): | |
293 isdst = self._isdst(dt) | |
294 | |
295 if isdst is None: | |
296 return None | |
297 elif isdst: | |
298 return self._dst_offset | |
299 else: | |
300 return self._std_offset | |
301 | |
302 def dst(self, dt): | |
303 isdst = self._isdst(dt) | |
304 | |
305 if isdst is None: | |
306 return None | |
307 elif isdst: | |
308 return self._dst_base_offset | |
309 else: | |
310 return ZERO | |
311 | |
312 @tzname_in_python2 | |
313 def tzname(self, dt): | |
314 if self._isdst(dt): | |
315 return self._dst_abbr | |
316 else: | |
317 return self._std_abbr | |
318 | |
319 def fromutc(self, dt): | |
320 """ Given a datetime in UTC, return local time """ | |
321 if not isinstance(dt, datetime): | |
322 raise TypeError("fromutc() requires a datetime argument") | |
323 | |
324 if dt.tzinfo is not self: | |
325 raise ValueError("dt.tzinfo is not self") | |
326 | |
327 # Get transitions - if there are none, fixed offset | |
328 transitions = self.transitions(dt.year) | |
329 if transitions is None: | |
330 return dt + self.utcoffset(dt) | |
331 | |
332 # Get the transition times in UTC | |
333 dston, dstoff = transitions | |
334 | |
335 dston -= self._std_offset | |
336 dstoff -= self._std_offset | |
337 | |
338 utc_transitions = (dston, dstoff) | |
339 dt_utc = dt.replace(tzinfo=None) | |
340 | |
341 isdst = self._naive_isdst(dt_utc, utc_transitions) | |
342 | |
343 if isdst: | |
344 dt_wall = dt + self._dst_offset | |
345 else: | |
346 dt_wall = dt + self._std_offset | |
347 | |
348 _fold = int(not isdst and self.is_ambiguous(dt_wall)) | |
349 | |
350 return enfold(dt_wall, fold=_fold) | |
351 | |
352 def is_ambiguous(self, dt): | |
353 """ | |
354 Whether or not the "wall time" of a given datetime is ambiguous in this | |
355 zone. | |
356 | |
357 :param dt: | |
358 A :py:class:`datetime.datetime`, naive or time zone aware. | |
359 | |
360 | |
361 :return: | |
362 Returns ``True`` if ambiguous, ``False`` otherwise. | |
363 | |
364 .. versionadded:: 2.6.0 | |
365 """ | |
366 if not self.hasdst: | |
367 return False | |
368 | |
369 start, end = self.transitions(dt.year) | |
370 | |
371 dt = dt.replace(tzinfo=None) | |
372 return (end <= dt < end + self._dst_base_offset) | |
373 | |
374 def _isdst(self, dt): | |
375 if not self.hasdst: | |
376 return False | |
377 elif dt is None: | |
378 return None | |
379 | |
380 transitions = self.transitions(dt.year) | |
381 | |
382 if transitions is None: | |
383 return False | |
384 | |
385 dt = dt.replace(tzinfo=None) | |
386 | |
387 isdst = self._naive_isdst(dt, transitions) | |
388 | |
389 # Handle ambiguous dates | |
390 if not isdst and self.is_ambiguous(dt): | |
391 return not self._fold(dt) | |
392 else: | |
393 return isdst | |
394 | |
395 def _naive_isdst(self, dt, transitions): | |
396 dston, dstoff = transitions | |
397 | |
398 dt = dt.replace(tzinfo=None) | |
399 | |
400 if dston < dstoff: | |
401 isdst = dston <= dt < dstoff | |
402 else: | |
403 isdst = not dstoff <= dt < dston | |
404 | |
405 return isdst | |
406 | |
407 @property | |
408 def _dst_base_offset(self): | |
409 return self._dst_offset - self._std_offset | |
410 | |
411 __hash__ = None | |
412 | |
413 def __ne__(self, other): | |
414 return not (self == other) | |
415 | |
416 def __repr__(self): | |
417 return "%s(...)" % self.__class__.__name__ | |
418 | |
419 __reduce__ = object.__reduce__ |