dateutilx 0.3.0__tar.gz → 0.4.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.3.0 → dateutilx-0.4.0}/PKG-INFO +2 -2
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/parser/_parser.py +7 -7
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/parser/isoparser.py +23 -12
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/relativedelta.py +8 -7
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/rrule.py +15 -16
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/tz/tz.py +11 -4
- {dateutilx-0.3.0 → dateutilx-0.4.0}/.base_project/LICENSE +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/.gitignore +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/LICENSE +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/README.md +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/__init__.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/easter.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/errors.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/info.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/parser/__init__.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/parser/utils.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/tz/__init__.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/tz/_common.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/tz/_factories.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/tz/_win.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/tz/win.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/utils/__init__.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/utils/constants.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/utils/funcs.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/dateutilx/utils/weekday.py +0 -0
- {dateutilx-0.3.0 → dateutilx-0.4.0}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: dateutilx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Modern extension to the standard Python datetime module
|
|
5
5
|
Project-URL: Homepage, https://github.com/rsb-23/dateutilx
|
|
6
6
|
Project-URL: Documentation, https://dateutilx.readthedocs.io/en/stable/
|
|
@@ -391,15 +391,16 @@ class _YMD(list):
|
|
|
391
391
|
return 1 <= value <= monthrange(year, month)[1]
|
|
392
392
|
|
|
393
393
|
def append(self, val, label=None):
|
|
394
|
+
_valid_labels = (None, "Y")
|
|
394
395
|
if hasattr(val, "__len__"):
|
|
395
396
|
if val.isdigit() and len(val) > 2:
|
|
396
397
|
self.century_specified = True
|
|
397
|
-
if label not in
|
|
398
|
+
if label not in _valid_labels: # pragma: no cover
|
|
398
399
|
raise ValueError(label)
|
|
399
400
|
label = "Y"
|
|
400
401
|
elif val > 100:
|
|
401
402
|
self.century_specified = True
|
|
402
|
-
if label not in
|
|
403
|
+
if label not in _valid_labels: # pragma: no cover
|
|
403
404
|
raise ValueError(label)
|
|
404
405
|
label = "Y"
|
|
405
406
|
|
|
@@ -426,7 +427,7 @@ class _YMD(list):
|
|
|
426
427
|
if len(self) == 3 and len(strids) == 2:
|
|
427
428
|
# we can back out the remaining stridx value
|
|
428
429
|
missing = [x for x in range(3) if x not in strids.values()]
|
|
429
|
-
key = [x for x in
|
|
430
|
+
key = [x for x in ("y", "m", "d") if x not in strids]
|
|
430
431
|
assert len(missing) == len(key) == 1
|
|
431
432
|
key = key[0]
|
|
432
433
|
val = missing[0]
|
|
@@ -1022,8 +1023,7 @@ class Parser:
|
|
|
1022
1023
|
def _could_be_tzname(self, hour, tzname, tzoffset, token):
|
|
1023
1024
|
return (
|
|
1024
1025
|
hour is not None
|
|
1025
|
-
and tzname is None
|
|
1026
|
-
and tzoffset is None
|
|
1026
|
+
and tzname is tzoffset is None
|
|
1027
1027
|
and len(token) <= 5
|
|
1028
1028
|
and (all(x in string.ascii_uppercase for x in token) or token in self.info.UTCZONE)
|
|
1029
1029
|
)
|
|
@@ -1370,7 +1370,7 @@ class _TzParser:
|
|
|
1370
1370
|
|
|
1371
1371
|
used_idxs.extend([*range(j)])
|
|
1372
1372
|
i = j
|
|
1373
|
-
if i < len_l and (_tmp_list[i] in ("+", "-") or _tmp_list[i][0] in
|
|
1373
|
+
if i < len_l and (_tmp_list[i] in ("+", "-") or _tmp_list[i][0] in string.digits):
|
|
1374
1374
|
if _tmp_list[i] in ("+", "-"):
|
|
1375
1375
|
# Yes, that's right. See the TZ variable documentation.
|
|
1376
1376
|
signal = (1, -1)[_tmp_list[i] == "+"]
|
|
@@ -1462,7 +1462,7 @@ class _TzParser:
|
|
|
1462
1462
|
for x in _tmp_list[i:]
|
|
1463
1463
|
if x not in (",", "/", "J", "M", ".", "-", ":")
|
|
1464
1464
|
for y in x
|
|
1465
|
-
if y not in
|
|
1465
|
+
if y not in string.digits
|
|
1466
1466
|
]
|
|
1467
1467
|
):
|
|
1468
1468
|
for x in (res.start, res.end):
|
|
@@ -10,6 +10,7 @@ ISO-8601 specification.
|
|
|
10
10
|
import calendar
|
|
11
11
|
import datetime as dt
|
|
12
12
|
import re
|
|
13
|
+
import string
|
|
13
14
|
from functools import wraps
|
|
14
15
|
|
|
15
16
|
from dateutilx.tz import UTC, TzOffset
|
|
@@ -49,7 +50,7 @@ class IsoParser:
|
|
|
49
50
|
For strict ISO-8601 adherence, pass ``'T'``.
|
|
50
51
|
"""
|
|
51
52
|
if sep is not None:
|
|
52
|
-
if len(sep) != 1 or ord(sep) >= 128 or sep in
|
|
53
|
+
if len(sep) != 1 or ord(sep) >= 128 or sep in string.digits:
|
|
53
54
|
raise ValueError("Separator must be a single, non-numeric " + "ASCII character")
|
|
54
55
|
|
|
55
56
|
sep = sep.encode("ascii")
|
|
@@ -202,6 +203,13 @@ class IsoParser:
|
|
|
202
203
|
_TIME_SEP = b":"
|
|
203
204
|
_FRACTION_REGEX = re.compile(b"[.,]([0-9]+)")
|
|
204
205
|
|
|
206
|
+
def _parse_digits(self, s, pos, n):
|
|
207
|
+
"""Parse exactly ``n`` ASCII digits starting at ``pos``."""
|
|
208
|
+
digits = s[pos : pos + n]
|
|
209
|
+
if len(digits) != n or not digits.isdigit():
|
|
210
|
+
raise ValueError("Expected {} digits, got {!r}".format(n, digits.decode("ascii")))
|
|
211
|
+
return int(digits)
|
|
212
|
+
|
|
205
213
|
def _parse_isodate(self, dt_str: str) -> tuple:
|
|
206
214
|
if len(dt_str) < 4:
|
|
207
215
|
raise ValueError("ISO string too short")
|
|
@@ -216,7 +224,7 @@ class IsoParser:
|
|
|
216
224
|
components = [1, 1, 1]
|
|
217
225
|
|
|
218
226
|
# Year
|
|
219
|
-
components[0] =
|
|
227
|
+
components[0] = self._parse_digits(dt_str, 0, 4)
|
|
220
228
|
pos = 4
|
|
221
229
|
if pos >= len_str:
|
|
222
230
|
return components, pos
|
|
@@ -229,7 +237,7 @@ class IsoParser:
|
|
|
229
237
|
if len_str - pos < 2:
|
|
230
238
|
raise ValueError("Invalid common month")
|
|
231
239
|
|
|
232
|
-
components[1] =
|
|
240
|
+
components[1] = self._parse_digits(dt_str, pos, 2)
|
|
233
241
|
pos += 2
|
|
234
242
|
|
|
235
243
|
if pos >= len_str:
|
|
@@ -245,12 +253,12 @@ class IsoParser:
|
|
|
245
253
|
# Day
|
|
246
254
|
if len_str - pos < 2:
|
|
247
255
|
raise ValueError("Invalid common day")
|
|
248
|
-
components[2] =
|
|
256
|
+
components[2] = self._parse_digits(dt_str, pos, 2)
|
|
249
257
|
return components, pos + 2
|
|
250
258
|
|
|
251
259
|
def _parse_isodate_uncommon(self, dt_str: str) -> tuple:
|
|
252
260
|
# All ISO formats start with the year
|
|
253
|
-
year =
|
|
261
|
+
year = self._parse_digits(dt_str, 0, 4)
|
|
254
262
|
|
|
255
263
|
has_sep = dt_str[4:5] == self._DATE_SEP
|
|
256
264
|
|
|
@@ -258,7 +266,7 @@ class IsoParser:
|
|
|
258
266
|
if dt_str[pos : pos + 1] == b"W":
|
|
259
267
|
# YYYY-?Www-?D?
|
|
260
268
|
pos += 1
|
|
261
|
-
weekno =
|
|
269
|
+
weekno = self._parse_digits(dt_str, pos, 2)
|
|
262
270
|
pos += 2
|
|
263
271
|
|
|
264
272
|
dayno = 1
|
|
@@ -268,7 +276,7 @@ class IsoParser:
|
|
|
268
276
|
|
|
269
277
|
pos += has_sep
|
|
270
278
|
|
|
271
|
-
dayno =
|
|
279
|
+
dayno = self._parse_digits(dt_str, pos, 1)
|
|
272
280
|
pos += 1
|
|
273
281
|
|
|
274
282
|
base_date = self._calculate_weekdate(year, weekno, dayno)
|
|
@@ -277,7 +285,7 @@ class IsoParser:
|
|
|
277
285
|
if len(dt_str) - pos < 3:
|
|
278
286
|
raise ValueError("Invalid ordinal day")
|
|
279
287
|
|
|
280
|
-
ordinal_day =
|
|
288
|
+
ordinal_day = self._parse_digits(dt_str, pos, 3)
|
|
281
289
|
pos += 3
|
|
282
290
|
|
|
283
291
|
if ordinal_day < 1 or ordinal_day > (365 + calendar.isleap(year)):
|
|
@@ -347,7 +355,7 @@ class IsoParser:
|
|
|
347
355
|
|
|
348
356
|
if comp < 3:
|
|
349
357
|
# Hour, minute, second
|
|
350
|
-
components[comp] =
|
|
358
|
+
components[comp] = self._parse_digits(timestr, pos, 2)
|
|
351
359
|
pos += 2
|
|
352
360
|
|
|
353
361
|
if comp == 3:
|
|
@@ -383,13 +391,16 @@ class IsoParser:
|
|
|
383
391
|
else:
|
|
384
392
|
raise ValueError("Time zone offset requires sign")
|
|
385
393
|
|
|
386
|
-
hours =
|
|
394
|
+
hours = self._parse_digits(tzstr, 1, 2)
|
|
387
395
|
if len(tzstr) == 3:
|
|
388
396
|
minutes = 0
|
|
389
397
|
else:
|
|
390
|
-
|
|
398
|
+
pos = 4 if tzstr[3:4] == self._TIME_SEP else 3
|
|
399
|
+
if len(tzstr) != pos + 2:
|
|
400
|
+
raise ValueError("Invalid minutes in time zone offset")
|
|
401
|
+
minutes = self._parse_digits(tzstr, pos, 2)
|
|
391
402
|
|
|
392
|
-
if zero_as_utc and hours ==
|
|
403
|
+
if zero_as_utc and hours == minutes == 0:
|
|
393
404
|
return UTC
|
|
394
405
|
|
|
395
406
|
if minutes > 59:
|
|
@@ -172,7 +172,7 @@ class RelativeDelta:
|
|
|
172
172
|
|
|
173
173
|
# Remove the year/month delta so the timedelta is just well-defined
|
|
174
174
|
# time units (seconds, days and microseconds)
|
|
175
|
-
dtm = self
|
|
175
|
+
dtm = self + dt2
|
|
176
176
|
|
|
177
177
|
# If we've overshot our target, make an adjustment
|
|
178
178
|
if dt1 < dt2:
|
|
@@ -185,7 +185,7 @@ class RelativeDelta:
|
|
|
185
185
|
while compare(dt1, dtm):
|
|
186
186
|
months += increment
|
|
187
187
|
self._set_months(months)
|
|
188
|
-
dtm = self
|
|
188
|
+
dtm = self + dt2
|
|
189
189
|
|
|
190
190
|
# Get the timedelta between the "months-adjusted" date and dt1
|
|
191
191
|
delta = dt1 - dtm
|
|
@@ -212,14 +212,15 @@ class RelativeDelta:
|
|
|
212
212
|
|
|
213
213
|
yday = nlyearday or yearday
|
|
214
214
|
|
|
215
|
-
if yearday and not nlyearday and yearday
|
|
215
|
+
if yearday and not nlyearday and (59 < yearday < 366):
|
|
216
|
+
# Shifting for leapday except Dec 32/31
|
|
216
217
|
self.leapdays = -1
|
|
217
218
|
|
|
218
219
|
if yday:
|
|
219
220
|
if not 1 <= yday <= 366:
|
|
220
221
|
raise ValueError(f"invalid year day ({yday})")
|
|
221
222
|
|
|
222
|
-
resolved = dt.date(2001, 1, 1) + dt.timedelta(days=yday - 1)
|
|
223
|
+
resolved = dt.date(2001, 1, 1) + dt.timedelta(days=min(yday, 365) - 1)
|
|
223
224
|
self.day, self.month = resolved.day, resolved.month
|
|
224
225
|
|
|
225
226
|
def _absolutes(self) -> dict[str, Number]:
|
|
@@ -393,7 +394,7 @@ class RelativeDelta:
|
|
|
393
394
|
month += 12
|
|
394
395
|
day = min(calendar.monthrange(year, month)[1], self.day or other.day)
|
|
395
396
|
repl = {"year": year, "month": month, "day": day}
|
|
396
|
-
for attr in
|
|
397
|
+
for attr in ("hour", "minute", "second", "microsecond"):
|
|
397
398
|
value = getattr(self, attr)
|
|
398
399
|
if value is not None:
|
|
399
400
|
repl[attr] = value
|
|
@@ -512,11 +513,11 @@ class RelativeDelta:
|
|
|
512
513
|
|
|
513
514
|
def __repr__(self):
|
|
514
515
|
_tmp_list = []
|
|
515
|
-
for attr in
|
|
516
|
+
for attr in ("years", "months", "days", "leapdays", "hours", "minutes", "seconds", "microseconds"):
|
|
516
517
|
value = getattr(self, attr)
|
|
517
518
|
if value:
|
|
518
519
|
_tmp_list.append(f"{attr}={value:+g}")
|
|
519
|
-
for attr in
|
|
520
|
+
for attr in ("year", "month", "day", "weekday", "hour", "minute", "second", "microsecond"):
|
|
520
521
|
value = getattr(self, attr)
|
|
521
522
|
if value is not None:
|
|
522
523
|
_tmp_list.append(f"{attr}={value!r}")
|
|
@@ -106,21 +106,20 @@ class RruleBase:
|
|
|
106
106
|
i = 0
|
|
107
107
|
gen = self._cache_gen
|
|
108
108
|
cache = self._cache
|
|
109
|
-
|
|
110
|
-
release = self._cache_lock.release
|
|
109
|
+
|
|
111
110
|
while gen:
|
|
112
111
|
if i == len(cache):
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
112
|
+
with self._cache_lock:
|
|
113
|
+
if self._cache_complete:
|
|
114
|
+
break
|
|
115
|
+
try:
|
|
116
|
+
for _ in range(10):
|
|
117
|
+
cache.append(next(gen))
|
|
118
|
+
except StopIteration:
|
|
119
|
+
self._cache_gen = gen = None
|
|
120
|
+
self._cache_complete = True
|
|
121
|
+
break
|
|
122
|
+
|
|
124
123
|
yield cache[i]
|
|
125
124
|
i += 1
|
|
126
125
|
while i < self._len:
|
|
@@ -459,7 +458,7 @@ class Rrule(RruleBase):
|
|
|
459
458
|
if self._bysetpos:
|
|
460
459
|
self._original_rule["bysetpos"] = self._bysetpos
|
|
461
460
|
|
|
462
|
-
if
|
|
461
|
+
if all(x is None for x in (byweekno, byyearday, bymonthday, byweekday, byeaster)):
|
|
463
462
|
if freq == YEARLY:
|
|
464
463
|
if bymonth is None:
|
|
465
464
|
bymonth = dtstart.month
|
|
@@ -660,7 +659,7 @@ class Rrule(RruleBase):
|
|
|
660
659
|
original_rule = self._original_rule
|
|
661
660
|
|
|
662
661
|
partfmt = "{name}={vals}"
|
|
663
|
-
for name, key in
|
|
662
|
+
for name, key in (
|
|
664
663
|
("BYSETPOS", "bysetpos"),
|
|
665
664
|
("BYMONTH", "bymonth"),
|
|
666
665
|
("BYMONTHDAY", "bymonthday"),
|
|
@@ -671,7 +670,7 @@ class Rrule(RruleBase):
|
|
|
671
670
|
("BYMINUTE", "byminute"),
|
|
672
671
|
("BYSECOND", "bysecond"),
|
|
673
672
|
("BYEASTER", "byeaster"),
|
|
674
|
-
|
|
673
|
+
):
|
|
675
674
|
value = original_rule.get(key)
|
|
676
675
|
if value:
|
|
677
676
|
parts.append(partfmt.format(name=name, vals=(",".join(str(v) for v in value))))
|
|
@@ -11,6 +11,7 @@ import bisect
|
|
|
11
11
|
import contextlib
|
|
12
12
|
import datetime
|
|
13
13
|
import os
|
|
14
|
+
import string
|
|
14
15
|
import struct
|
|
15
16
|
import threading
|
|
16
17
|
import time
|
|
@@ -67,7 +68,9 @@ class TzUTC(datetime.tzinfo, metaclass=_TzSingleton):
|
|
|
67
68
|
|
|
68
69
|
__name__ = "tzutc"
|
|
69
70
|
__hash__ = None
|
|
70
|
-
|
|
71
|
+
|
|
72
|
+
def __reduce__(self):
|
|
73
|
+
return self.__class__, tuple()
|
|
71
74
|
|
|
72
75
|
def utcoffset(self, dt):
|
|
73
76
|
return ZERO
|
|
@@ -172,7 +175,8 @@ class TzOffset(datetime.tzinfo, metaclass=_TzOffsetFactory):
|
|
|
172
175
|
def __repr__(self):
|
|
173
176
|
return f"{self.__class__.__name__}({self._name!r}, {int(self._offset.total_seconds())})"
|
|
174
177
|
|
|
175
|
-
|
|
178
|
+
def __reduce__(self):
|
|
179
|
+
return self.__class__, (self._name, self._offset)
|
|
176
180
|
|
|
177
181
|
|
|
178
182
|
class TzLocal(_TzInfo):
|
|
@@ -191,7 +195,7 @@ class TzLocal(_TzInfo):
|
|
|
191
195
|
|
|
192
196
|
self._dst_saved = self._dst_offset - self._std_offset
|
|
193
197
|
self._hasdst = bool(self._dst_saved)
|
|
194
|
-
self._tznames =
|
|
198
|
+
self._tznames = time.tzname
|
|
195
199
|
|
|
196
200
|
def utcoffset(self, dt):
|
|
197
201
|
if dt is None and self._hasdst:
|
|
@@ -1231,6 +1235,9 @@ class TzIcal:
|
|
|
1231
1235
|
else:
|
|
1232
1236
|
signal = +1
|
|
1233
1237
|
|
|
1238
|
+
if not (s.isascii() and s.isdigit()):
|
|
1239
|
+
raise ValueError("invalid offset: " + s)
|
|
1240
|
+
|
|
1234
1241
|
if len(s) == 4:
|
|
1235
1242
|
return (int(s[:2]) * 3600 + int(s[2:]) * 60) * signal
|
|
1236
1243
|
if len(s) == 6:
|
|
@@ -1537,7 +1544,7 @@ def __get_gettz():
|
|
|
1537
1544
|
# one offset. For short values of "name", an
|
|
1538
1545
|
# explicit for loop seems to be the fastest way
|
|
1539
1546
|
# To determine if a string contains a digit
|
|
1540
|
-
if c in
|
|
1547
|
+
if c in string.digits:
|
|
1541
1548
|
with contextlib.suppress(ValueError):
|
|
1542
1549
|
tz = TzStr(name)
|
|
1543
1550
|
break
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|