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/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
from ._version import version as __version__
|
|
5
|
+
except ImportError:
|
|
6
|
+
__version__ = 'unknown'
|
|
7
|
+
|
|
8
|
+
__all__ = ["easter", "parser", "relativedelta", "rrule", "tz", "utils"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def __getattr__(name):
|
|
12
|
+
import importlib
|
|
13
|
+
|
|
14
|
+
if name in __all__ or name == "zoneinfo":
|
|
15
|
+
return importlib.import_module("." + name, __name__)
|
|
16
|
+
raise AttributeError(
|
|
17
|
+
"module {!r} has no attribute {!r}".format(__name__, name)
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def __dir__():
|
|
22
|
+
# __dir__ should include all the lazy-importable modules as well.
|
|
23
|
+
return (
|
|
24
|
+
[x for x in globals() if x not in sys.modules] + __all__ + ["zoneinfo"]
|
|
25
|
+
)
|
dateutil/_common.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Common code used in multiple modules.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class weekday:
|
|
7
|
+
__slots__ = ["weekday", "n"]
|
|
8
|
+
|
|
9
|
+
def __init__(self, weekday, n=None):
|
|
10
|
+
self.weekday = weekday
|
|
11
|
+
self.n = n
|
|
12
|
+
|
|
13
|
+
def __call__(self, n):
|
|
14
|
+
if n == self.n:
|
|
15
|
+
return self
|
|
16
|
+
else:
|
|
17
|
+
return self.__class__(self.weekday, n)
|
|
18
|
+
|
|
19
|
+
def __eq__(self, other):
|
|
20
|
+
try:
|
|
21
|
+
if self.weekday != other.weekday or self.n != other.n:
|
|
22
|
+
return False
|
|
23
|
+
except AttributeError:
|
|
24
|
+
return False
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
def __hash__(self):
|
|
28
|
+
return hash((
|
|
29
|
+
self.weekday,
|
|
30
|
+
self.n,
|
|
31
|
+
))
|
|
32
|
+
|
|
33
|
+
def __repr__(self):
|
|
34
|
+
s = ("MO", "TU", "WE", "TH", "FR", "SA", "SU")[self.weekday]
|
|
35
|
+
if not self.n:
|
|
36
|
+
return s
|
|
37
|
+
else:
|
|
38
|
+
return "%s(%+d)" % (s, self.n)
|
|
39
|
+
|
|
40
|
+
# vim:ts=4:sw=4:et
|
dateutil/_tzdata_impl.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""This is an implementation of functions that interact with the tzdata module.
|
|
2
|
+
|
|
3
|
+
It will only live as long as :mod:`dateutil.zoneinfo` lives; when the
|
|
4
|
+
``dateutil.zoneinfo`` module is removed, this can all move under :mod:`tz`.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib.resources
|
|
8
|
+
from typing import BinaryIO, TextIO
|
|
9
|
+
|
|
10
|
+
_TZDATA_LOAD_EXCEPTIONS = (
|
|
11
|
+
ImportError,
|
|
12
|
+
FileNotFoundError,
|
|
13
|
+
UnicodeEncodeError,
|
|
14
|
+
IsADirectoryError,
|
|
15
|
+
ValueError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _open_text(package: str, resource: str) -> TextIO:
|
|
20
|
+
return (
|
|
21
|
+
importlib.resources.files(package)
|
|
22
|
+
.joinpath(resource)
|
|
23
|
+
.open("r", encoding="utf-8")
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _open_binary(package: str, resource: str) -> BinaryIO:
|
|
28
|
+
return importlib.resources.files(package).joinpath(resource).open("rb")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _is_package_directory(package: str, resource: str) -> bool:
|
|
32
|
+
"""Checks for directories before opening resources on Windows."""
|
|
33
|
+
try:
|
|
34
|
+
return importlib.resources.files(package).joinpath(resource).is_dir()
|
|
35
|
+
except ImportError:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _load_tzdata(key):
|
|
40
|
+
components = key.split("/")
|
|
41
|
+
package_name = ".".join(["tzdata.zoneinfo"] + components[:-1])
|
|
42
|
+
resource_name = components[-1]
|
|
43
|
+
|
|
44
|
+
# Empty or relative path components cannot name a zone, and they produce
|
|
45
|
+
# module names that the import machinery handles inconsistently across
|
|
46
|
+
# versions, so treat them as not found up front. A key naming a directory
|
|
47
|
+
# (a subpackage) is not a zone either, and opening it raises a
|
|
48
|
+
# platform-dependent error, see CPython gh-85702.
|
|
49
|
+
if any(c in ("", ".", "..") for c in components) or _is_package_directory(
|
|
50
|
+
package_name, resource_name
|
|
51
|
+
):
|
|
52
|
+
raise TZFileNotFound("Time zone not found: %s", zone_key=key)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
return _open_binary(package_name, resource_name)
|
|
56
|
+
except _TZDATA_LOAD_EXCEPTIONS:
|
|
57
|
+
# ImportError: package does not exist (or tzdata is not installed)
|
|
58
|
+
# FileNotFoundError: the key is not a file in the package
|
|
59
|
+
# UnicodeEncodeError: the key is not encodable as a filename
|
|
60
|
+
# IsADirectoryError: the key names a package rather than a resource
|
|
61
|
+
# ValueError: the key is not a valid path (e.g. contains a NUL byte)
|
|
62
|
+
raise TZFileNotFound("Time zone not found: %s", zone_key=key) from None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _load_tzdata_keys():
|
|
66
|
+
with _open_text("tzdata", "zones") as f:
|
|
67
|
+
return list(filter(None, (l.strip() for l in f)))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TZFileNotFound(ValueError):
|
|
71
|
+
"""Indicates that a time zone file isn't present in tzdata."""
|
|
72
|
+
|
|
73
|
+
def __init__(self, msg: str, *args: object, zone_key: str) -> None:
|
|
74
|
+
super().__init__(msg % zone_key, *args)
|
|
75
|
+
self.zone_key = zone_key
|
dateutil/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '4.0.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (4, 0, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
dateutil/easter.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module offers a generic Easter computing method for any given year, using
|
|
3
|
+
Western, Orthodox or Julian algorithms.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
|
|
8
|
+
__all__ = ["easter", "EASTER_JULIAN", "EASTER_ORTHODOX", "EASTER_WESTERN"]
|
|
9
|
+
|
|
10
|
+
EASTER_JULIAN = 1
|
|
11
|
+
EASTER_ORTHODOX = 2
|
|
12
|
+
EASTER_WESTERN = 3
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def easter(year, method=EASTER_WESTERN):
|
|
16
|
+
"""
|
|
17
|
+
This method was ported from the work done by GM Arts,
|
|
18
|
+
on top of the algorithm by Claus Tondering, which was
|
|
19
|
+
based in part on the algorithm of Ouding (1940), as
|
|
20
|
+
quoted in "Explanatory Supplement to the Astronomical
|
|
21
|
+
Almanac", P. Kenneth Seidelmann, editor.
|
|
22
|
+
|
|
23
|
+
This algorithm implements three different Easter
|
|
24
|
+
calculation methods:
|
|
25
|
+
|
|
26
|
+
1. Original calculation in Julian calendar, valid in
|
|
27
|
+
dates after 326 AD
|
|
28
|
+
2. Original method, with date converted to Gregorian
|
|
29
|
+
calendar, valid in years 1583 to 4099
|
|
30
|
+
3. Revised method, in Gregorian calendar, valid in
|
|
31
|
+
years 1583 to 4099 as well
|
|
32
|
+
|
|
33
|
+
These methods are represented by the constants:
|
|
34
|
+
|
|
35
|
+
* ``EASTER_JULIAN = 1``
|
|
36
|
+
* ``EASTER_ORTHODOX = 2``
|
|
37
|
+
* ``EASTER_WESTERN = 3``
|
|
38
|
+
|
|
39
|
+
The default method is method 3.
|
|
40
|
+
|
|
41
|
+
More about the algorithm may be found at:
|
|
42
|
+
|
|
43
|
+
`GM Arts: Easter Algorithms <http://www.gmarts.org/index.php?go=415>`_
|
|
44
|
+
|
|
45
|
+
and
|
|
46
|
+
|
|
47
|
+
`The Calendar FAQ: Easter <https://www.tondering.dk/claus/cal/easter.php>`_
|
|
48
|
+
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
if not (1 <= method <= 3):
|
|
52
|
+
raise ValueError("invalid method")
|
|
53
|
+
|
|
54
|
+
# g - Golden year - 1
|
|
55
|
+
# c - Century
|
|
56
|
+
# h - (23 - Epact) mod 30
|
|
57
|
+
# i - Number of days from March 21 to Paschal Full Moon
|
|
58
|
+
# j - Weekday for PFM (0=Sunday, etc)
|
|
59
|
+
# p - Number of days from March 21 to Sunday on or before PFM
|
|
60
|
+
# (-6 to 28 methods 1 & 3, to 56 for method 2)
|
|
61
|
+
# e - Extra days to add for method 2 (converting Julian
|
|
62
|
+
# date to Gregorian date)
|
|
63
|
+
|
|
64
|
+
y = year
|
|
65
|
+
g = y % 19
|
|
66
|
+
e = 0
|
|
67
|
+
if method < 3:
|
|
68
|
+
# Old method
|
|
69
|
+
i = (19*g + 15) % 30
|
|
70
|
+
j = (y + y//4 + i) % 7
|
|
71
|
+
if method == 2:
|
|
72
|
+
# Extra dates to convert Julian to Gregorian date
|
|
73
|
+
e = 10
|
|
74
|
+
if y > 1600:
|
|
75
|
+
e = e + y//100 - 16 - (y//100 - 16)//4
|
|
76
|
+
else:
|
|
77
|
+
# New method
|
|
78
|
+
c = y//100
|
|
79
|
+
h = (c - c//4 - (8*c + 13)//25 + 19*g + 15) % 30
|
|
80
|
+
i = h - (h//28)*(1 - (h//28)*(29//(h + 1))*((21 - g)//11))
|
|
81
|
+
j = (y + y//4 + i + 2 - c + c//4) % 7
|
|
82
|
+
|
|
83
|
+
# p can be from -6 to 56 corresponding to dates 22 March to 23 May
|
|
84
|
+
# (later dates apply to method 2, although 23 May never actually occurs)
|
|
85
|
+
p = i - j + e
|
|
86
|
+
d = 1 + (p + 27 + (p + 6)//40) % 31
|
|
87
|
+
m = 3 + (p + 26)//30
|
|
88
|
+
return datetime.date(int(y), int(m), int(d))
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from ._parser import (
|
|
2
|
+
DEFAULTPARSER,
|
|
3
|
+
DEFAULTTZPARSER,
|
|
4
|
+
ParserError,
|
|
5
|
+
UnknownTimezoneWarning,
|
|
6
|
+
__doc__,
|
|
7
|
+
parse,
|
|
8
|
+
parser,
|
|
9
|
+
parserinfo,
|
|
10
|
+
)
|
|
11
|
+
from .isoparser import isoparse, isoparser
|
|
12
|
+
|
|
13
|
+
__all__ = ['parse', 'parser', 'parserinfo',
|
|
14
|
+
'isoparse', 'isoparser',
|
|
15
|
+
'ParserError',
|
|
16
|
+
'UnknownTimezoneWarning']
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
###
|
|
20
|
+
# Deprecate portions of the private interface so that downstream code that
|
|
21
|
+
# is improperly relying on it is given *some* notice.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def __deprecated_private_func(f):
|
|
25
|
+
import warnings
|
|
26
|
+
from functools import wraps
|
|
27
|
+
|
|
28
|
+
msg = ('{name} is a private function and may break without warning, '
|
|
29
|
+
'it will be moved and or renamed in future versions.')
|
|
30
|
+
msg = msg.format(name=f.__name__)
|
|
31
|
+
|
|
32
|
+
@wraps(f)
|
|
33
|
+
def deprecated_func(*args, **kwargs):
|
|
34
|
+
warnings.warn(msg, DeprecationWarning)
|
|
35
|
+
return f(*args, **kwargs)
|
|
36
|
+
|
|
37
|
+
return deprecated_func
|
|
38
|
+
|
|
39
|
+
def __deprecate_private_class(c):
|
|
40
|
+
import warnings
|
|
41
|
+
|
|
42
|
+
msg = ('{name} is a private class and may break without warning, '
|
|
43
|
+
'it will be moved and or renamed in future versions.')
|
|
44
|
+
msg = msg.format(name=c.__name__)
|
|
45
|
+
|
|
46
|
+
class private_class(c):
|
|
47
|
+
__doc__ = c.__doc__
|
|
48
|
+
|
|
49
|
+
def __init__(self, *args, **kwargs):
|
|
50
|
+
warnings.warn(msg, DeprecationWarning)
|
|
51
|
+
super().__init__(*args, **kwargs)
|
|
52
|
+
|
|
53
|
+
private_class.__name__ = c.__name__
|
|
54
|
+
|
|
55
|
+
return private_class
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
from ._parser import _parsetz, _resultbase, _timelex, _tzparser
|
|
59
|
+
|
|
60
|
+
_timelex = __deprecate_private_class(_timelex)
|
|
61
|
+
_tzparser = __deprecate_private_class(_tzparser)
|
|
62
|
+
_resultbase = __deprecate_private_class(_resultbase)
|
|
63
|
+
_parsetz = __deprecated_private_func(_parsetz)
|