dateutilx 0.1.1__tar.gz → 0.2.0__tar.gz
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.
- {dateutilx-0.1.1 → dateutilx-0.2.0}/.gitignore +1 -1
- {dateutilx-0.1.1 → dateutilx-0.2.0}/PKG-INFO +10 -27
- {dateutilx-0.1.1 → dateutilx-0.2.0}/README.md +7 -23
- {dateutilx-0.1.1 → dateutilx-0.2.0}/pyproject.toml +7 -7
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/easter.py +2 -2
- dateutilx-0.2.0/src/helper/__init__.py +4 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/helper/constants.py +0 -5
- dateutilx-0.2.0/src/helper/funcs.py +5 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/parser/_parser.py +69 -71
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/parser/isoparser.py +24 -29
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/parser/utils.py +3 -4
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/relativedelta.py +150 -244
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/rrule.py +131 -152
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tz/_win.py +22 -21
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tz/tz.py +111 -110
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tz/win.py +1 -1
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tzwin.py +1 -1
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/utils.py +5 -6
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/weekday.py +6 -6
- dateutilx-0.1.1/src/dateutil/__init__.py +0 -5
- dateutilx-0.1.1/src/dateutilx/helper/__init__.py +0 -1
- {dateutilx-0.1.1 → dateutilx-0.2.0}/.base_project/LICENSE +0 -0
- {dateutilx-0.1.1 → dateutilx-0.2.0}/LICENSE +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/__init__.py +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/errors.py +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/info.py +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/parser/__init__.py +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tz/__init__.py +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tz/_common.py +0 -0
- {dateutilx-0.1.1/src/dateutilx → dateutilx-0.2.0/src}/tz/_factories.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dateutilx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Modern extension to the standard Python datetime module
|
|
5
5
|
Project-URL: Homepage, https://github.com/rsb-23/dateutilx
|
|
6
|
-
Project-URL: Documentation, https://
|
|
6
|
+
Project-URL: Documentation, https://dateutilx.readthedocs.io/en/stable/
|
|
7
7
|
Project-URL: Source, https://github.com/rsb-23/dateutilx
|
|
8
8
|
Author: Rishabh B
|
|
9
9
|
License: Copyright 2017- Paul Ganssle <paul@ganssle.io>
|
|
@@ -72,20 +72,19 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
72
72
|
Classifier: License :: OSI Approved :: BSD License
|
|
73
73
|
Classifier: Programming Language :: Python
|
|
74
74
|
Classifier: Programming Language :: Python :: 3
|
|
75
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
76
75
|
Classifier: Programming Language :: Python :: 3.11
|
|
77
76
|
Classifier: Programming Language :: Python :: 3.12
|
|
78
77
|
Classifier: Programming Language :: Python :: 3.13
|
|
79
78
|
Classifier: Programming Language :: Python :: 3.14
|
|
80
79
|
Classifier: Programming Language :: Python :: 3.15
|
|
81
80
|
Classifier: Topic :: Software Development :: Libraries
|
|
82
|
-
Requires-Python: >=3.
|
|
81
|
+
Requires-Python: >=3.11
|
|
83
82
|
Requires-Dist: tzdata; sys_platform == 'win32'
|
|
84
83
|
Description-Content-Type: text/markdown
|
|
85
84
|
|
|
86
85
|
> [!NOTE]
|
|
87
|
-
> This package is rewrite of python-dateutil
|
|
88
|
-
> It is not related with the base project and its authors.
|
|
86
|
+
> This package is rewrite of `python-dateutil v2.9.0.post0` as base, to support active python versions only.
|
|
87
|
+
> It is not related with the base project and its authors.
|
|
89
88
|
> Docs of base project are under `.base_project/`
|
|
90
89
|
|
|
91
90
|
# dateutilx - powerful extensions to datetime
|
|
@@ -127,7 +126,7 @@ https://github.com/rsb-23/dateutilx/
|
|
|
127
126
|
* Computing of dates based on very flexible recurrence rules, using a superset of
|
|
128
127
|
the [iCalendar](https://www.ietf.org/rfc/rfc2445.txt) specification. Parsing of RFC strings is supported as well.
|
|
129
128
|
* Generic parsing of dates in almost any string format;
|
|
130
|
-
* Timezone (tzinfo) implementations for tzfile(5) format files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
|
|
129
|
+
* Timezone (tzinfo) implementations for tzfile (5) format files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
|
|
131
130
|
environment string (in all known formats), iCalendar format files, given ranges (with help from relative deltas),
|
|
132
131
|
local machine timezone, fixed offset timezone, UTC timezone, and Windows registry-based time zones.
|
|
133
132
|
* Internal up-to-date world timezone information based on Olson's database.
|
|
@@ -136,13 +135,10 @@ https://github.com/rsb-23/dateutilx/
|
|
|
136
135
|
|
|
137
136
|
## Quick example
|
|
138
137
|
|
|
139
|
-
Here's a snapshot, just to give an idea about the power of the
|
|
140
|
-
package. For more examples, look at the documentation.
|
|
138
|
+
Here's a snapshot, just to give an idea about the power of the package. For more examples, look at the documentation.
|
|
141
139
|
|
|
142
|
-
Suppose you want to know how much time is left, in
|
|
143
|
-
|
|
144
|
-
year with a Friday 13th in August, and you want to get today's
|
|
145
|
-
date out of the "date" unix system command. Here is the code:
|
|
140
|
+
Suppose you want to know how much time is left, in years/months/days/etc, before the next easter happening on a year
|
|
141
|
+
with a Friday 13th in August, and you want to get today's date out of the "date" unix system command. Here is the code:
|
|
146
142
|
|
|
147
143
|
```pycon
|
|
148
144
|
>>> from datetime import *
|
|
@@ -174,14 +170,7 @@ more information about how to contribute to the project, see the `CONTRIBUTING.m
|
|
|
174
170
|
|
|
175
171
|
## Author
|
|
176
172
|
|
|
177
|
-
The
|
|
178
|
-
|
|
179
|
-
It was maintained by:
|
|
180
|
-
|
|
181
|
-
* Gustavo Niemeyer <gustavo@niemeyer.net> 2003-2011
|
|
182
|
-
* Tomi Pieviläinen <tomi.pievilainen@iki.fi> 2012-2014
|
|
183
|
-
* Yaron de Leeuw <me@jarondl.net> 2014-2016
|
|
184
|
-
* Paul Ganssle <paul@ganssle.io> 2015-
|
|
173
|
+
The dateutilx module is improved/re-written by rsb-23 in 2026.
|
|
185
174
|
|
|
186
175
|
## License
|
|
187
176
|
|
|
@@ -192,17 +181,11 @@ those those explicitly relicensed - are released only under the BSD 3-Clause Lic
|
|
|
192
181
|
|
|
193
182
|
|
|
194
183
|
[license]: https://github.com/rsb-23/dateutilx/blob/main/LICENSE
|
|
195
|
-
|
|
196
184
|
[lint-badge]: https://github.com/rsb-23/dateutilx/actions/workflows/code-lint.yml/badge.svg
|
|
197
|
-
|
|
198
185
|
[tests-badge]: https://github.com/rsb-23/dateutilx/actions/workflows/code-test.yml/badge.svg
|
|
199
186
|
|
|
200
187
|
[license-badge]: https://img.shields.io/pypi/l/dateutilx.svg?style=flat-square
|
|
201
|
-
|
|
202
188
|
[pre-commit-badge]: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white
|
|
203
|
-
|
|
204
189
|
[py-version]: https://img.shields.io/pypi/pyversions/dateutilx
|
|
205
|
-
|
|
206
190
|
[pypi-downloads]: https://img.shields.io/pypi/dm/dateutilx?label=Downloads
|
|
207
|
-
|
|
208
191
|
[pypi-v]: https://img.shields.io/pypi/v/dateutilx?label=pypi
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
> [!NOTE]
|
|
2
|
-
> This package is rewrite of python-dateutil
|
|
3
|
-
> It is not related with the base project and its authors.
|
|
2
|
+
> This package is rewrite of `python-dateutil v2.9.0.post0` as base, to support active python versions only.
|
|
3
|
+
> It is not related with the base project and its authors.
|
|
4
4
|
> Docs of base project are under `.base_project/`
|
|
5
5
|
|
|
6
6
|
# dateutilx - powerful extensions to datetime
|
|
@@ -42,7 +42,7 @@ https://github.com/rsb-23/dateutilx/
|
|
|
42
42
|
* Computing of dates based on very flexible recurrence rules, using a superset of
|
|
43
43
|
the [iCalendar](https://www.ietf.org/rfc/rfc2445.txt) specification. Parsing of RFC strings is supported as well.
|
|
44
44
|
* Generic parsing of dates in almost any string format;
|
|
45
|
-
* Timezone (tzinfo) implementations for tzfile(5) format files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
|
|
45
|
+
* Timezone (tzinfo) implementations for tzfile (5) format files (/etc/localtime, /usr/share/zoneinfo, etc), TZ
|
|
46
46
|
environment string (in all known formats), iCalendar format files, given ranges (with help from relative deltas),
|
|
47
47
|
local machine timezone, fixed offset timezone, UTC timezone, and Windows registry-based time zones.
|
|
48
48
|
* Internal up-to-date world timezone information based on Olson's database.
|
|
@@ -51,13 +51,10 @@ https://github.com/rsb-23/dateutilx/
|
|
|
51
51
|
|
|
52
52
|
## Quick example
|
|
53
53
|
|
|
54
|
-
Here's a snapshot, just to give an idea about the power of the
|
|
55
|
-
package. For more examples, look at the documentation.
|
|
54
|
+
Here's a snapshot, just to give an idea about the power of the package. For more examples, look at the documentation.
|
|
56
55
|
|
|
57
|
-
Suppose you want to know how much time is left, in
|
|
58
|
-
|
|
59
|
-
year with a Friday 13th in August, and you want to get today's
|
|
60
|
-
date out of the "date" unix system command. Here is the code:
|
|
56
|
+
Suppose you want to know how much time is left, in years/months/days/etc, before the next easter happening on a year
|
|
57
|
+
with a Friday 13th in August, and you want to get today's date out of the "date" unix system command. Here is the code:
|
|
61
58
|
|
|
62
59
|
```pycon
|
|
63
60
|
>>> from datetime import *
|
|
@@ -89,14 +86,7 @@ more information about how to contribute to the project, see the `CONTRIBUTING.m
|
|
|
89
86
|
|
|
90
87
|
## Author
|
|
91
88
|
|
|
92
|
-
The
|
|
93
|
-
|
|
94
|
-
It was maintained by:
|
|
95
|
-
|
|
96
|
-
* Gustavo Niemeyer <gustavo@niemeyer.net> 2003-2011
|
|
97
|
-
* Tomi Pieviläinen <tomi.pievilainen@iki.fi> 2012-2014
|
|
98
|
-
* Yaron de Leeuw <me@jarondl.net> 2014-2016
|
|
99
|
-
* Paul Ganssle <paul@ganssle.io> 2015-
|
|
89
|
+
The dateutilx module is improved/re-written by rsb-23 in 2026.
|
|
100
90
|
|
|
101
91
|
## License
|
|
102
92
|
|
|
@@ -107,17 +97,11 @@ those those explicitly relicensed - are released only under the BSD 3-Clause Lic
|
|
|
107
97
|
|
|
108
98
|
|
|
109
99
|
[license]: https://github.com/rsb-23/dateutilx/blob/main/LICENSE
|
|
110
|
-
|
|
111
100
|
[lint-badge]: https://github.com/rsb-23/dateutilx/actions/workflows/code-lint.yml/badge.svg
|
|
112
|
-
|
|
113
101
|
[tests-badge]: https://github.com/rsb-23/dateutilx/actions/workflows/code-test.yml/badge.svg
|
|
114
102
|
|
|
115
103
|
[license-badge]: https://img.shields.io/pypi/l/dateutilx.svg?style=flat-square
|
|
116
|
-
|
|
117
104
|
[pre-commit-badge]: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white
|
|
118
|
-
|
|
119
105
|
[py-version]: https://img.shields.io/pypi/pyversions/dateutilx
|
|
120
|
-
|
|
121
106
|
[pypi-downloads]: https://img.shields.io/pypi/dm/dateutilx?label=Downloads
|
|
122
|
-
|
|
123
107
|
[pypi-v]: https://img.shields.io/pypi/v/dateutilx?label=pypi
|
|
@@ -18,7 +18,6 @@ classifiers = [
|
|
|
18
18
|
"License :: OSI Approved :: Apache Software License",
|
|
19
19
|
"Programming Language :: Python",
|
|
20
20
|
"Programming Language :: Python :: 3",
|
|
21
|
-
"Programming Language :: Python :: 3.10",
|
|
22
21
|
"Programming Language :: Python :: 3.11",
|
|
23
22
|
"Programming Language :: Python :: 3.12",
|
|
24
23
|
"Programming Language :: Python :: 3.13",
|
|
@@ -27,7 +26,7 @@ classifiers = [
|
|
|
27
26
|
"Topic :: Software Development :: Libraries",
|
|
28
27
|
]
|
|
29
28
|
|
|
30
|
-
requires-python = ">=3.
|
|
29
|
+
requires-python = ">=3.11"
|
|
31
30
|
dependencies = ["tzdata; sys_platform == 'win32'"]
|
|
32
31
|
|
|
33
32
|
[dependency-groups]
|
|
@@ -37,21 +36,22 @@ test = ["hypothesis", "pytest", "pytest-cov"]
|
|
|
37
36
|
|
|
38
37
|
[project.urls]
|
|
39
38
|
Homepage = "https://github.com/rsb-23/dateutilx"
|
|
40
|
-
Documentation = "https://
|
|
39
|
+
Documentation = "https://dateutilx.readthedocs.io/en/stable/"
|
|
41
40
|
Source = "https://github.com/rsb-23/dateutilx"
|
|
42
41
|
|
|
43
42
|
# Build Config
|
|
44
43
|
[tool.hatch.version]
|
|
45
44
|
source = "vcs"
|
|
46
45
|
[tool.hatch.build.targets.sdist]
|
|
47
|
-
include = ["src
|
|
46
|
+
include = ["src", "LICENSE", "README.md", "pyproject.toml"]
|
|
48
47
|
[tool.hatch.build.targets.wheel]
|
|
49
|
-
packages = ["src
|
|
48
|
+
packages = ["src"]
|
|
50
49
|
|
|
50
|
+
[tool.coverage.run]
|
|
51
|
+
source = ["src"]
|
|
51
52
|
[tool.coverage.report]
|
|
52
53
|
fail_under = 85.0
|
|
53
|
-
|
|
54
|
-
source = ["src/"]
|
|
54
|
+
skip_covered = true
|
|
55
55
|
|
|
56
56
|
[tool.pytest.ini_options]
|
|
57
57
|
python_files = ["test_*.py", "*_test.py", "*_solution.py", ]
|
|
@@ -66,7 +66,7 @@ def easter(year, method: EasterAlgo | int = EasterAlgo.WESTERN):
|
|
|
66
66
|
# e - Extra days to add for method 2 (converting Julian
|
|
67
67
|
# date to Gregorian date)
|
|
68
68
|
|
|
69
|
-
y = year
|
|
69
|
+
y = int(year)
|
|
70
70
|
g = y % 19
|
|
71
71
|
e = 0
|
|
72
72
|
if method < 3:
|
|
@@ -90,4 +90,4 @@ def easter(year, method: EasterAlgo | int = EasterAlgo.WESTERN):
|
|
|
90
90
|
p = i - j + e
|
|
91
91
|
d = 1 + (p + 27 + (p + 6) // 40) % 31
|
|
92
92
|
m = 3 + (p + 26) // 30
|
|
93
|
-
return datetime.date(
|
|
93
|
+
return datetime.date(y, m, d)
|
|
@@ -34,12 +34,13 @@ import string
|
|
|
34
34
|
import time
|
|
35
35
|
import warnings
|
|
36
36
|
from calendar import monthrange
|
|
37
|
+
from collections import deque
|
|
37
38
|
from decimal import Decimal
|
|
38
39
|
from io import StringIO
|
|
39
40
|
from warnings import warn
|
|
40
41
|
|
|
41
|
-
from
|
|
42
|
-
from
|
|
42
|
+
from src import relativedelta, tz
|
|
43
|
+
from src.errors import ParserError, UnknownTimezoneWarning
|
|
43
44
|
|
|
44
45
|
from .utils import standard_dt_parser
|
|
45
46
|
|
|
@@ -65,7 +66,7 @@ class _TimeLex:
|
|
|
65
66
|
|
|
66
67
|
self.instream = instream
|
|
67
68
|
self.charstack = []
|
|
68
|
-
self.tokenstack =
|
|
69
|
+
self.tokenstack = deque()
|
|
69
70
|
self.eof = False
|
|
70
71
|
|
|
71
72
|
def get_token(self):
|
|
@@ -83,7 +84,7 @@ class _TimeLex:
|
|
|
83
84
|
demands that multiple tokens be parsed at once.
|
|
84
85
|
"""
|
|
85
86
|
if self.tokenstack:
|
|
86
|
-
return self.tokenstack.
|
|
87
|
+
return self.tokenstack.popleft()
|
|
87
88
|
|
|
88
89
|
seenletters = False
|
|
89
90
|
token = None
|
|
@@ -309,10 +310,7 @@ class ParserInfo:
|
|
|
309
310
|
return self._hms.get(name.lower())
|
|
310
311
|
|
|
311
312
|
def ampm(self, name):
|
|
312
|
-
|
|
313
|
-
return self._ampm[name.lower()]
|
|
314
|
-
except KeyError:
|
|
315
|
-
return None
|
|
313
|
+
return self._ampm.get(name.lower())
|
|
316
314
|
|
|
317
315
|
def pertain(self, name):
|
|
318
316
|
return name.lower() in self._pertain
|
|
@@ -564,8 +562,8 @@ class Parser:
|
|
|
564
562
|
.. doctest::
|
|
565
563
|
:options: +NORMALIZE_WHITESPACE
|
|
566
564
|
|
|
567
|
-
>>> from
|
|
568
|
-
>>> from
|
|
565
|
+
>>> from src.parser import parse
|
|
566
|
+
>>> from src.tz import gettz
|
|
569
567
|
>>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
|
|
570
568
|
>>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
|
|
571
569
|
datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
|
|
@@ -634,7 +632,7 @@ class Parser:
|
|
|
634
632
|
]
|
|
635
633
|
# fmt: on
|
|
636
634
|
|
|
637
|
-
def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False, fuzzy_with_tokens=False):
|
|
635
|
+
def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy: bool = False, fuzzy_with_tokens: bool = False):
|
|
638
636
|
"""
|
|
639
637
|
Private method which performs the heavy lifting of parsing, called from
|
|
640
638
|
``parse()``, which passes on its ``kwargs`` to this function.
|
|
@@ -669,7 +667,7 @@ class Parser:
|
|
|
669
667
|
|
|
670
668
|
.. doctest::
|
|
671
669
|
|
|
672
|
-
>>> from
|
|
670
|
+
>>> from src.parser import parse
|
|
673
671
|
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
|
|
674
672
|
(datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
|
|
675
673
|
|
|
@@ -678,12 +676,8 @@ class Parser:
|
|
|
678
676
|
fuzzy = True
|
|
679
677
|
|
|
680
678
|
info = self.info
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
dayfirst = info.dayfirst
|
|
684
|
-
|
|
685
|
-
if yearfirst is None:
|
|
686
|
-
yearfirst = info.yearfirst
|
|
679
|
+
dayfirst = dayfirst or info.dayfirst
|
|
680
|
+
yearfirst = yearfirst or info.yearfirst
|
|
687
681
|
|
|
688
682
|
res = self._Result()
|
|
689
683
|
_tokens = _TimeLex.split(timestr) # Splits the timestr into tokens
|
|
@@ -706,7 +700,7 @@ class Parser:
|
|
|
706
700
|
|
|
707
701
|
if value is not None:
|
|
708
702
|
# Numeric token
|
|
709
|
-
i = self._parse_numeric_token(_tokens, i, info, ymd, res, fuzzy)
|
|
703
|
+
i = self._parse_numeric_token(_tokens, i, info=info, ymd=ymd, res=res, fuzzy=fuzzy)
|
|
710
704
|
|
|
711
705
|
# Check weekday
|
|
712
706
|
elif info.weekday(_tokens[i]) is not None:
|
|
@@ -800,13 +794,14 @@ class Parser:
|
|
|
800
794
|
res.tzoffset = signal * (hour_offset * 3600 + min_offset * 60)
|
|
801
795
|
|
|
802
796
|
# Look for a timezone name between parenthesis
|
|
803
|
-
if (
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
797
|
+
if i + 5 < len_l and all(
|
|
798
|
+
(
|
|
799
|
+
info.jump(_tokens[i + 2]),
|
|
800
|
+
_tokens[i + 3] == "(",
|
|
801
|
+
_tokens[i + 5] == ")",
|
|
802
|
+
len(_tokens[i + 4]) >= 3,
|
|
803
|
+
self._could_be_tzname(res.hour, res.tzname, None, _tokens[i + 4]),
|
|
804
|
+
)
|
|
810
805
|
):
|
|
811
806
|
# -0300 (BRST)
|
|
812
807
|
res.tzname = _tokens[i + 4]
|
|
@@ -842,7 +837,7 @@ class Parser:
|
|
|
842
837
|
|
|
843
838
|
return res, None
|
|
844
839
|
|
|
845
|
-
def _parse_numeric_token(self, tokens, idx, info, ymd, res, fuzzy):
|
|
840
|
+
def _parse_numeric_token(self, tokens, idx, *, info, ymd, res, fuzzy):
|
|
846
841
|
# Token is a number
|
|
847
842
|
value_repr = tokens[idx]
|
|
848
843
|
try:
|
|
@@ -854,11 +849,13 @@ class Parser:
|
|
|
854
849
|
|
|
855
850
|
len_l = len(tokens)
|
|
856
851
|
|
|
857
|
-
if (
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
852
|
+
if all(
|
|
853
|
+
(
|
|
854
|
+
len(ymd) == 3,
|
|
855
|
+
len_li in (2, 4),
|
|
856
|
+
res.hour is None,
|
|
857
|
+
(idx + 1 >= len_l or (tokens[idx + 1] != ":" and info.hms(tokens[idx + 1]) is None)),
|
|
858
|
+
)
|
|
862
859
|
):
|
|
863
860
|
# 19990101T23[59]
|
|
864
861
|
s = tokens[idx]
|
|
@@ -1271,8 +1268,8 @@ def parse(timestr, parser_info=None, **kwargs):
|
|
|
1271
1268
|
.. doctest::
|
|
1272
1269
|
:options: +NORMALIZE_WHITESPACE
|
|
1273
1270
|
|
|
1274
|
-
>>> from
|
|
1275
|
-
>>> from
|
|
1271
|
+
>>> from src.parser import parse
|
|
1272
|
+
>>> from src.tz import gettz
|
|
1276
1273
|
>>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
|
|
1277
1274
|
>>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
|
|
1278
1275
|
datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
|
|
@@ -1308,7 +1305,7 @@ def parse(timestr, parser_info=None, **kwargs):
|
|
|
1308
1305
|
|
|
1309
1306
|
.. doctest::
|
|
1310
1307
|
|
|
1311
|
-
>>> from
|
|
1308
|
+
>>> from src.parser import parse
|
|
1312
1309
|
>>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
|
|
1313
1310
|
(datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
|
|
1314
1311
|
|
|
@@ -1349,8 +1346,9 @@ class _TzParser:
|
|
|
1349
1346
|
self.end = self._Attr()
|
|
1350
1347
|
|
|
1351
1348
|
def parse(self, tzstr):
|
|
1349
|
+
tzstr_pattern = re.compile(r"([,:.]|[a-zA-Z]+|[0-9]+)")
|
|
1352
1350
|
res = self._Result()
|
|
1353
|
-
_tmp_list = [x for x in re.split(
|
|
1351
|
+
_tmp_list = [x for x in re.split(tzstr_pattern, tzstr) if x]
|
|
1354
1352
|
used_idxs = []
|
|
1355
1353
|
try:
|
|
1356
1354
|
i, len_l = 0, len(_tmp_list)
|
|
@@ -1360,43 +1358,43 @@ class _TzParser:
|
|
|
1360
1358
|
j = i
|
|
1361
1359
|
while j < len_l and not [x for x in _tmp_list[j] if x in "0123456789:,-+"]:
|
|
1362
1360
|
j += 1
|
|
1363
|
-
if j
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
else:
|
|
1380
|
-
signal = -1
|
|
1381
|
-
len_li = len(_tmp_list[i])
|
|
1382
|
-
if len_li == 4:
|
|
1383
|
-
# -0300
|
|
1384
|
-
setattr(res, offattr, (int(_tmp_list[i][:2]) * 3600 + int(_tmp_list[i][2:]) * 60) * signal)
|
|
1385
|
-
elif i + 1 < len_l and _tmp_list[i + 1] == ":":
|
|
1386
|
-
# -03:00
|
|
1387
|
-
setattr(res, offattr, (int(_tmp_list[i]) * 3600 + int(_tmp_list[i + 2]) * 60) * signal)
|
|
1388
|
-
used_idxs.append(i)
|
|
1389
|
-
i += 2
|
|
1390
|
-
elif len_li <= 2:
|
|
1391
|
-
# -[0]3
|
|
1392
|
-
setattr(res, offattr, int(_tmp_list[i][:2]) * 3600 * signal)
|
|
1393
|
-
else:
|
|
1394
|
-
return None
|
|
1361
|
+
if j == i:
|
|
1362
|
+
break
|
|
1363
|
+
|
|
1364
|
+
if not res.stdabbr:
|
|
1365
|
+
offattr = "stdoffset"
|
|
1366
|
+
res.stdabbr = "".join(_tmp_list[i:j])
|
|
1367
|
+
else:
|
|
1368
|
+
offattr = "dstoffset"
|
|
1369
|
+
res.dstabbr = "".join(_tmp_list[i:j])
|
|
1370
|
+
|
|
1371
|
+
used_idxs.extend([*range(j)])
|
|
1372
|
+
i = j
|
|
1373
|
+
if i < len_l and (_tmp_list[i] in ("+", "-") or _tmp_list[i][0] in "0123456789"):
|
|
1374
|
+
if _tmp_list[i] in ("+", "-"):
|
|
1375
|
+
# Yes, that's right. See the TZ variable documentation.
|
|
1376
|
+
signal = (1, -1)[_tmp_list[i] == "+"]
|
|
1395
1377
|
used_idxs.append(i)
|
|
1396
1378
|
i += 1
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1379
|
+
else:
|
|
1380
|
+
signal = -1
|
|
1381
|
+
len_li = len(_tmp_list[i])
|
|
1382
|
+
if len_li == 4:
|
|
1383
|
+
# -0300
|
|
1384
|
+
setattr(res, offattr, (int(_tmp_list[i][:2]) * 3600 + int(_tmp_list[i][2:]) * 60) * signal)
|
|
1385
|
+
elif i + 1 < len_l and _tmp_list[i + 1] == ":":
|
|
1386
|
+
# -03:00
|
|
1387
|
+
setattr(res, offattr, (int(_tmp_list[i]) * 3600 + int(_tmp_list[i + 2]) * 60) * signal)
|
|
1388
|
+
used_idxs.append(i)
|
|
1389
|
+
i += 2
|
|
1390
|
+
elif len_li <= 2:
|
|
1391
|
+
# -[0]3
|
|
1392
|
+
setattr(res, offattr, int(_tmp_list[i][:2]) * 3600 * signal)
|
|
1393
|
+
else:
|
|
1394
|
+
return None
|
|
1395
|
+
used_idxs.append(i)
|
|
1396
|
+
i += 1
|
|
1397
|
+
if res.dstabbr:
|
|
1400
1398
|
break
|
|
1401
1399
|
|
|
1402
1400
|
if i < len_l:
|
|
@@ -8,11 +8,11 @@ ISO-8601 specification.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import calendar
|
|
11
|
+
import datetime as dt
|
|
11
12
|
import re
|
|
12
|
-
from datetime import date, datetime, time, timedelta
|
|
13
13
|
from functools import wraps
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from src.tz import UTC, TzOffset
|
|
16
16
|
|
|
17
17
|
__all__ = ["isoparse", "isoparser"]
|
|
18
18
|
|
|
@@ -21,7 +21,10 @@ def _takes_ascii(f):
|
|
|
21
21
|
@wraps(f)
|
|
22
22
|
def func(self, str_in, *args, **kwargs):
|
|
23
23
|
# If it's a stream, read the whole thing
|
|
24
|
-
|
|
24
|
+
read = getattr(str_in, "read", None)
|
|
25
|
+
|
|
26
|
+
if read is not None:
|
|
27
|
+
str_in = read()
|
|
25
28
|
|
|
26
29
|
# If it's unicode, turn it into bytes, since ISO-8601 only covers ASCII
|
|
27
30
|
if isinstance(str_in, str):
|
|
@@ -104,10 +107,10 @@ class IsoParser:
|
|
|
104
107
|
- `±HHMM`
|
|
105
108
|
- `±HH`
|
|
106
109
|
|
|
107
|
-
Offsets will be represented as :class:`
|
|
110
|
+
Offsets will be represented as :class:`dateutilx.tz.tzoffset` objects,
|
|
108
111
|
with the exception of UTC, which will be represented as
|
|
109
|
-
:class:`
|
|
110
|
-
as `+00:00`) will also be represented as :class:`
|
|
112
|
+
:class:`dateutilx.tz.tzutc`. Time zone offsets equivalent to UTC (such
|
|
113
|
+
as `+00:00`) will also be represented as :class:`dateutilx.tz.tzutc`.
|
|
111
114
|
|
|
112
115
|
:param dt_str:
|
|
113
116
|
A string or stream containing only an ISO-8601 datetime string
|
|
@@ -138,9 +141,9 @@ class IsoParser:
|
|
|
138
141
|
|
|
139
142
|
if len(components) > 3 and components[3] == 24:
|
|
140
143
|
components[3] = 0
|
|
141
|
-
return datetime(*components) + timedelta(days=1)
|
|
144
|
+
return dt.datetime(*components) + dt.timedelta(days=1)
|
|
142
145
|
|
|
143
|
-
return datetime(*components)
|
|
146
|
+
return dt.datetime(*components)
|
|
144
147
|
|
|
145
148
|
@_takes_ascii
|
|
146
149
|
def parse_isodate(self, datestr):
|
|
@@ -156,7 +159,7 @@ class IsoParser:
|
|
|
156
159
|
components, pos = self._parse_isodate(datestr)
|
|
157
160
|
if pos < len(datestr):
|
|
158
161
|
raise ValueError(f"String contains unknown ISO components: {datestr.decode('ascii')!r}")
|
|
159
|
-
return date(*components)
|
|
162
|
+
return dt.date(*components)
|
|
160
163
|
|
|
161
164
|
@_takes_ascii
|
|
162
165
|
def parse_isotime(self, timestr):
|
|
@@ -172,7 +175,7 @@ class IsoParser:
|
|
|
172
175
|
components = self._parse_isotime(timestr)
|
|
173
176
|
if components[0] == 24:
|
|
174
177
|
components[0] = 0
|
|
175
|
-
return time(*components)
|
|
178
|
+
return dt.time(*components)
|
|
176
179
|
|
|
177
180
|
@_takes_ascii
|
|
178
181
|
def parse_tzstr(self, tzstr, zero_as_utc=True):
|
|
@@ -185,11 +188,11 @@ class IsoParser:
|
|
|
185
188
|
A string representing an ISO time zone offset
|
|
186
189
|
|
|
187
190
|
:param zero_as_utc:
|
|
188
|
-
Whether to return :class:`
|
|
191
|
+
Whether to return :class:`dateutilx.tz.tzutc` for zero-offset zones
|
|
189
192
|
|
|
190
193
|
:return:
|
|
191
|
-
Returns :class:`
|
|
192
|
-
:class:`
|
|
194
|
+
Returns :class:`dateutilx.tz.tzoffset` for offsets and
|
|
195
|
+
:class:`dateutilx.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is
|
|
193
196
|
specified) offsets equivalent to UTC.
|
|
194
197
|
"""
|
|
195
198
|
return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
|
|
@@ -199,19 +202,19 @@ class IsoParser:
|
|
|
199
202
|
_TIME_SEP = b":"
|
|
200
203
|
_FRACTION_REGEX = re.compile(b"[.,]([0-9]+)")
|
|
201
204
|
|
|
202
|
-
def _parse_isodate(self, dt_str):
|
|
205
|
+
def _parse_isodate(self, dt_str: str) -> tuple:
|
|
206
|
+
if len(dt_str) < 4:
|
|
207
|
+
raise ValueError("ISO string too short")
|
|
208
|
+
|
|
203
209
|
try:
|
|
204
210
|
return self._parse_isodate_common(dt_str)
|
|
205
211
|
except ValueError:
|
|
206
212
|
return self._parse_isodate_uncommon(dt_str)
|
|
207
213
|
|
|
208
|
-
def _parse_isodate_common(self, dt_str):
|
|
214
|
+
def _parse_isodate_common(self, dt_str: str) -> tuple:
|
|
209
215
|
len_str = len(dt_str)
|
|
210
216
|
components = [1, 1, 1]
|
|
211
217
|
|
|
212
|
-
if len_str < 4:
|
|
213
|
-
raise ValueError("ISO string too short")
|
|
214
|
-
|
|
215
218
|
# Year
|
|
216
219
|
components[0] = int(dt_str[:4])
|
|
217
220
|
pos = 4
|
|
@@ -245,10 +248,7 @@ class IsoParser:
|
|
|
245
248
|
components[2] = int(dt_str[pos : pos + 2])
|
|
246
249
|
return components, pos + 2
|
|
247
250
|
|
|
248
|
-
def _parse_isodate_uncommon(self, dt_str):
|
|
249
|
-
if len(dt_str) < 4:
|
|
250
|
-
raise ValueError("ISO string too short")
|
|
251
|
-
|
|
251
|
+
def _parse_isodate_uncommon(self, dt_str: str) -> tuple:
|
|
252
252
|
# All ISO formats start with the year
|
|
253
253
|
year = int(dt_str[:4])
|
|
254
254
|
|
|
@@ -283,7 +283,7 @@ class IsoParser:
|
|
|
283
283
|
if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)):
|
|
284
284
|
raise ValueError(f"Invalid ordinal day {ordinal_day} for year {year}")
|
|
285
285
|
|
|
286
|
-
base_date = date(year, 1, 1) + timedelta(days=ordinal_day - 1)
|
|
286
|
+
base_date = dt.date(year, 1, 1) + dt.timedelta(days=ordinal_day - 1)
|
|
287
287
|
|
|
288
288
|
components = [base_date.year, base_date.month, base_date.day]
|
|
289
289
|
return components, pos
|
|
@@ -315,12 +315,7 @@ class IsoParser:
|
|
|
315
315
|
raise ValueError(f"Invalid weekday: {day}")
|
|
316
316
|
|
|
317
317
|
# Get week 1 for the specific year:
|
|
318
|
-
|
|
319
|
-
week_1 = jan_4 - timedelta(days=jan_4.isocalendar()[2] - 1)
|
|
320
|
-
|
|
321
|
-
# Now add the specific number of weeks and days to get what we want
|
|
322
|
-
week_offset = (week - 1) * 7 + (day - 1)
|
|
323
|
-
return week_1 + timedelta(days=week_offset)
|
|
318
|
+
return dt.date.fromisocalendar(year, week, day)
|
|
324
319
|
|
|
325
320
|
def _parse_isotime(self, timestr):
|
|
326
321
|
len_str = len(timestr)
|