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.
- dateutil/__init__.py +25 -0
- dateutil/_common.py +40 -0
- dateutil/_tzdata_impl.py +75 -0
- dateutil/_version.py +24 -0
- dateutil/easter.py +88 -0
- dateutil/parser/__init__.py +63 -0
- dateutil/parser/_parser.py +1607 -0
- dateutil/parser/isoparser.py +426 -0
- dateutil/relativedelta.py +595 -0
- dateutil/rrule.py +1752 -0
- dateutil/tz/__init__.py +44 -0
- dateutil/tz/_common.py +308 -0
- dateutil/tz/_factories.py +78 -0
- dateutil/tz/_tzfile.py +1332 -0
- dateutil/tz/_tzpath.py +12 -0
- dateutil/tz/tz.py +1392 -0
- dateutil/tz/win.py +370 -0
- dateutil/tzwin.py +2 -0
- dateutil/utils.py +69 -0
- dateutil/zoneinfo/__init__.py +224 -0
- dateutil/zoneinfo/rebuild.py +42 -0
- python_dateutil-4.0.0.dist-info/METADATA +184 -0
- python_dateutil-4.0.0.dist-info/RECORD +26 -0
- python_dateutil-4.0.0.dist-info/WHEEL +4 -0
- python_dateutil-4.0.0.dist-info/licenses/AUTHORS.md +142 -0
- python_dateutil-4.0.0.dist-info/licenses/LICENSE +54 -0
dateutil/tz/__init__.py
ADDED
|
@@ -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
|