python-dateutil 4.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,44 @@
1
+ from . import _tzpath
2
+ from ._tzpath import reset_tzpath
3
+ from .tz import *
4
+ from .tz import __doc__
5
+
6
+ __all__ = [
7
+ "tzutc",
8
+ "tzoffset",
9
+ "tzlocal",
10
+ "tzfile",
11
+ "tzrange",
12
+ "tzstr",
13
+ "tzical",
14
+ "tzwin",
15
+ "tzwinlocal",
16
+ "gettz",
17
+ "enfold",
18
+ "datetime_ambiguous",
19
+ "datetime_exists",
20
+ "resolve_imaginary",
21
+ "available_iana_timezones",
22
+ "UTC",
23
+ "TZPATH",
24
+ "reset_tzpath",
25
+ "DeprecatedTzFormatWarning",
26
+ "DeprecatedTzKeyWarning",
27
+ ]
28
+
29
+
30
+ def __getattr__(name):
31
+ if name == "TZPATH":
32
+ return _tzpath.TZPATH
33
+ else:
34
+ raise AttributeError(
35
+ "Module %s has no attribute %s" % (repr(__name__), repr(name))
36
+ )
37
+
38
+
39
+ def __dir__():
40
+ return sorted(list(globals()) + ["TZPATH"])
41
+
42
+
43
+ class DeprecatedTzFormatWarning(Warning):
44
+ """Warning raised when time zones are parsed from deprecated formats."""
dateutil/tz/_common.py ADDED
@@ -0,0 +1,308 @@
1
+
2
+ from functools import wraps
3
+
4
+ from datetime import datetime, timedelta, tzinfo
5
+
6
+
7
+ ZERO = timedelta(0)
8
+
9
+ __all__ = ["enfold"]
10
+
11
+
12
+ # The following is adapted from Alexander Belopolsky's tz library
13
+ # https://github.com/abalkin/tz
14
+ def enfold(dt: datetime, fold: int = 1) -> datetime:
15
+ """Returns a datetime with the ``fold`` attribute set to ``fold``.
16
+
17
+ :param fold:
18
+ The value for the ``fold`` attribute, either 0 or 1.
19
+
20
+ :return:
21
+ A :class:`datetime.datetime` with the requested ``fold`` value.
22
+
23
+ .. versionadded:: 2.6.0
24
+ """
25
+ return dt.replace(fold=fold)
26
+
27
+
28
+ def _validate_fromutc_inputs(f):
29
+ """
30
+ The CPython version of ``fromutc`` checks that the input is a ``datetime``
31
+ object and that ``self`` is attached as its ``tzinfo``.
32
+ """
33
+ @wraps(f)
34
+ def fromutc(self, dt):
35
+ if not isinstance(dt, datetime):
36
+ raise TypeError("fromutc() requires a datetime argument")
37
+ if dt.tzinfo is not self:
38
+ raise ValueError("dt.tzinfo is not self")
39
+
40
+ return f(self, dt)
41
+
42
+ return fromutc
43
+
44
+
45
+ class _tzinfo(tzinfo):
46
+ """
47
+ Base class for all ``dateutil`` ``tzinfo`` objects.
48
+ """
49
+
50
+ def is_ambiguous(self, dt):
51
+ """
52
+ Whether or not the "wall time" of a given datetime is ambiguous in this
53
+ zone.
54
+
55
+ :param dt:
56
+ A :py:class:`datetime.datetime`, naive or time zone aware.
57
+
58
+
59
+ :return:
60
+ Returns ``True`` if ambiguous, ``False`` otherwise.
61
+
62
+ .. versionadded:: 2.6.0
63
+ """
64
+
65
+ dt = dt.replace(tzinfo=self)
66
+
67
+ wall_0 = enfold(dt, fold=0)
68
+ wall_1 = enfold(dt, fold=1)
69
+
70
+ same_offset = wall_0.utcoffset() == wall_1.utcoffset()
71
+ same_dt = wall_0.replace(tzinfo=None) == wall_1.replace(tzinfo=None)
72
+
73
+ return same_dt and not same_offset
74
+
75
+ def _fold_status(self, dt_utc, dt_wall):
76
+ """
77
+ Determine the fold status of a "wall" datetime, given a representation
78
+ of the same datetime as a (naive) UTC datetime. This is calculated based
79
+ on the assumption that ``dt.utcoffset() - dt.dst()`` is constant for all
80
+ datetimes, and that this offset is the actual number of hours separating
81
+ ``dt_utc`` and ``dt_wall``.
82
+
83
+ :param dt_utc:
84
+ Representation of the datetime as UTC
85
+
86
+ :param dt_wall:
87
+ Representation of the datetime as "wall time". This parameter must
88
+ either have a `fold` attribute or have a fold-naive
89
+ :class:`datetime.tzinfo` attached, otherwise the calculation may
90
+ fail.
91
+ """
92
+ if self.is_ambiguous(dt_wall):
93
+ delta_wall = dt_wall - dt_utc
94
+ _fold = int(delta_wall == (dt_utc.utcoffset() - dt_utc.dst()))
95
+ else:
96
+ _fold = 0
97
+
98
+ return _fold
99
+
100
+ def _fromutc(self, dt):
101
+ """
102
+ Given a timezone-aware datetime in a given timezone, calculates a
103
+ timezone-aware datetime in a new timezone.
104
+
105
+ Since this is the one time that we *know* we have an unambiguous
106
+ datetime object, we take this opportunity to determine whether the
107
+ datetime is ambiguous and in a "fold" state (e.g. if it's the first
108
+ occurrence, chronologically, of the ambiguous datetime).
109
+
110
+ :param dt:
111
+ A timezone-aware :class:`datetime.datetime` object.
112
+ """
113
+
114
+ # Re-implement the algorithm from Python's datetime.py
115
+ dtoff = dt.utcoffset()
116
+ if dtoff is None:
117
+ raise ValueError("fromutc() requires a non-None utcoffset() "
118
+ "result")
119
+
120
+ # The original datetime.py code assumes that `dst()` defaults to
121
+ # zero during ambiguous times. PEP 495 inverts this presumption, so
122
+ # select fold=1 when calculating the DST adjustment below.
123
+ dtdst = dt.dst()
124
+ if dtdst is None:
125
+ raise ValueError("fromutc() requires a non-None dst() result")
126
+ delta = dtoff - dtdst
127
+
128
+ dt += delta
129
+ # Set fold=1 so we can default to being in the fold for
130
+ # ambiguous dates.
131
+ dtdst = enfold(dt, fold=1).dst()
132
+ if dtdst is None:
133
+ raise ValueError("fromutc(): dt.dst gave inconsistent "
134
+ "results; cannot convert")
135
+ return dt + dtdst
136
+
137
+ @_validate_fromutc_inputs
138
+ def fromutc(self, dt):
139
+ """
140
+ Given a timezone-aware datetime in a given timezone, calculates a
141
+ timezone-aware datetime in a new timezone.
142
+
143
+ Since this is the one time that we *know* we have an unambiguous
144
+ datetime object, we take this opportunity to determine whether the
145
+ datetime is ambiguous and in a "fold" state (e.g. if it's the first
146
+ occurrence, chronologically, of the ambiguous datetime).
147
+
148
+ :param dt:
149
+ A timezone-aware :class:`datetime.datetime` object.
150
+ """
151
+ dt_wall = self._fromutc(dt)
152
+
153
+ # Calculate the fold status given the two datetimes.
154
+ _fold = self._fold_status(dt, dt_wall)
155
+
156
+ # Set the default fold value for ambiguous dates
157
+ return enfold(dt_wall, fold=_fold)
158
+
159
+
160
+ class tzrangebase(_tzinfo):
161
+ """
162
+ This is an abstract base class for time zones represented by an annual
163
+ transition into and out of DST. Child classes should implement the following
164
+ methods:
165
+
166
+ * ``__init__(self, *args, **kwargs)``
167
+ * ``transitions(self, year)`` - this is expected to return a tuple of
168
+ datetimes representing the DST on and off transitions in standard
169
+ time.
170
+
171
+ A fully initialized ``tzrangebase`` subclass should also provide the
172
+ following attributes:
173
+ * ``hasdst``: Boolean whether or not the zone uses DST.
174
+ * ``_dst_offset`` / ``_std_offset``: :class:`datetime.timedelta` objects
175
+ representing the respective UTC offsets.
176
+ * ``_dst_abbr`` / ``_std_abbr``: Strings representing the timezone short
177
+ abbreviations in DST and STD, respectively.
178
+ * ``_hasdst``: Whether or not the zone has DST.
179
+
180
+ .. versionadded:: 2.6.0
181
+ """
182
+ def __init__(self):
183
+ raise NotImplementedError('tzrangebase is an abstract base class')
184
+
185
+ def utcoffset(self, dt):
186
+ isdst = self._isdst(dt)
187
+
188
+ if isdst is None:
189
+ return None
190
+ elif isdst:
191
+ return self._dst_offset
192
+ else:
193
+ return self._std_offset
194
+
195
+ def dst(self, dt):
196
+ isdst = self._isdst(dt)
197
+
198
+ if isdst is None:
199
+ return None
200
+ elif isdst:
201
+ return self._dst_base_offset
202
+ else:
203
+ return ZERO
204
+
205
+ def tzname(self, dt):
206
+ if self._isdst(dt):
207
+ return self._dst_abbr
208
+ else:
209
+ return self._std_abbr
210
+
211
+ def fromutc(self, dt):
212
+ """ Given a datetime in UTC, return local time """
213
+ if not isinstance(dt, datetime):
214
+ raise TypeError("fromutc() requires a datetime argument")
215
+
216
+ if dt.tzinfo is not self:
217
+ raise ValueError("dt.tzinfo is not self")
218
+
219
+ # Get transitions - if there are none, fixed offset
220
+ transitions = self.transitions(dt.year)
221
+ if transitions is None:
222
+ return dt + self.utcoffset(dt)
223
+
224
+ # Get the transition times in UTC
225
+ dston, dstoff = transitions
226
+
227
+ dston -= self._std_offset
228
+ dstoff -= self._std_offset
229
+
230
+ utc_transitions = (dston, dstoff)
231
+ dt_utc = dt.replace(tzinfo=None)
232
+
233
+ isdst = self._naive_isdst(dt_utc, utc_transitions)
234
+
235
+ if isdst:
236
+ dt_wall = dt + self._dst_offset
237
+ else:
238
+ dt_wall = dt + self._std_offset
239
+
240
+ _fold = int(not isdst and self.is_ambiguous(dt_wall))
241
+
242
+ return enfold(dt_wall, fold=_fold)
243
+
244
+ def is_ambiguous(self, dt):
245
+ """
246
+ Whether or not the "wall time" of a given datetime is ambiguous in this
247
+ zone.
248
+
249
+ :param dt:
250
+ A :py:class:`datetime.datetime`, naive or time zone aware.
251
+
252
+
253
+ :return:
254
+ Returns ``True`` if ambiguous, ``False`` otherwise.
255
+
256
+ .. versionadded:: 2.6.0
257
+ """
258
+ if not self.hasdst:
259
+ return False
260
+
261
+ start, end = self.transitions(dt.year)
262
+
263
+ dt = dt.replace(tzinfo=None)
264
+ return (end <= dt < end + self._dst_base_offset)
265
+
266
+ def _isdst(self, dt):
267
+ if not self.hasdst:
268
+ return False
269
+ elif dt is None:
270
+ return None
271
+
272
+ transitions = self.transitions(dt.year)
273
+
274
+ if transitions is None:
275
+ return False
276
+
277
+ dt = dt.replace(tzinfo=None)
278
+
279
+ isdst = self._naive_isdst(dt, transitions)
280
+
281
+ # Handle ambiguous dates
282
+ if not isdst and self.is_ambiguous(dt):
283
+ return not dt.fold
284
+ else:
285
+ return isdst
286
+
287
+ def _naive_isdst(self, dt, transitions):
288
+ dston, dstoff = transitions
289
+
290
+ dt = dt.replace(tzinfo=None)
291
+
292
+ if dston < dstoff:
293
+ isdst = dston <= dt < dstoff
294
+ else:
295
+ isdst = not dstoff <= dt < dston
296
+
297
+ return isdst
298
+
299
+ @property
300
+ def _dst_base_offset(self):
301
+ return self._dst_offset - self._std_offset
302
+
303
+ __hash__ = None
304
+
305
+ def __repr__(self):
306
+ return "%s(...)" % self.__class__.__name__
307
+
308
+ __reduce__ = object.__reduce__
@@ -0,0 +1,78 @@
1
+ import _thread
2
+ import weakref
3
+ from collections import OrderedDict
4
+ from datetime import timedelta
5
+
6
+
7
+ class _TzSingleton(type):
8
+ def __init__(cls, *args, **kwargs):
9
+ cls.__instance = None
10
+ super().__init__(*args, **kwargs)
11
+
12
+ def __call__(cls):
13
+ if cls.__instance is None:
14
+ cls.__instance = super().__call__()
15
+ return cls.__instance
16
+
17
+
18
+ class _TzFactory(type):
19
+ def instance(cls, *args, **kwargs):
20
+ """Alternate constructor that returns a fresh instance"""
21
+ return type.__call__(cls, *args, **kwargs)
22
+
23
+
24
+ class _TzOffsetFactory(_TzFactory):
25
+ def __init__(cls, *args, **kwargs):
26
+ cls.__instances = weakref.WeakValueDictionary()
27
+ cls.__strong_cache = OrderedDict()
28
+ cls.__strong_cache_size = 8
29
+
30
+ cls._cache_lock = _thread.allocate_lock()
31
+
32
+ def __call__(cls, name, offset):
33
+ if isinstance(offset, timedelta):
34
+ key = (name, offset.total_seconds())
35
+ else:
36
+ key = (name, offset)
37
+
38
+ instance = cls.__instances.get(key, None)
39
+ if instance is None:
40
+ instance = cls.__instances.setdefault(key,
41
+ cls.instance(name, offset))
42
+
43
+ # This lock may not be necessary in Python 3. See GH issue #901
44
+ with cls._cache_lock:
45
+ cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
46
+
47
+ # Remove an item if the strong cache is overpopulated
48
+ if len(cls.__strong_cache) > cls.__strong_cache_size:
49
+ cls.__strong_cache.popitem(last=False)
50
+
51
+ return instance
52
+
53
+
54
+ class _TzStrFactory(_TzFactory):
55
+ def __init__(cls, *args, **kwargs):
56
+ cls.__instances = weakref.WeakValueDictionary()
57
+ cls.__strong_cache = OrderedDict()
58
+ cls.__strong_cache_size = 8
59
+
60
+ cls.__cache_lock = _thread.allocate_lock()
61
+
62
+ def __call__(cls, s, posix_offset=False):
63
+ key = (s, posix_offset)
64
+ instance = cls.__instances.get(key, None)
65
+
66
+ if instance is None:
67
+ instance = cls.__instances.setdefault(key,
68
+ cls.instance(s, posix_offset))
69
+
70
+ # This lock may not be necessary in Python 3. See GH issue #901
71
+ with cls.__cache_lock:
72
+ cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
73
+
74
+ # Remove an item if the strong cache is overpopulated
75
+ if len(cls.__strong_cache) > cls.__strong_cache_size:
76
+ cls.__strong_cache.popitem(last=False)
77
+
78
+ return instance