dateutilx 0.2.0__tar.gz → 0.3.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.
Files changed (30) hide show
  1. {dateutilx-0.2.0 → dateutilx-0.3.0}/.gitignore +0 -3
  2. {dateutilx-0.2.0 → dateutilx-0.3.0}/PKG-INFO +1 -1
  3. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/__init__.py +1 -1
  4. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/parser/_parser.py +12 -12
  5. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/parser/isoparser.py +7 -7
  6. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/relativedelta.py +51 -34
  7. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/rrule.py +10 -11
  8. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/tz/__init__.py +5 -14
  9. dateutilx-0.3.0/dateutilx/tz/_factories.py +61 -0
  10. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/tz/_win.py +5 -5
  11. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/tz/tz.py +20 -24
  12. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/tz/win.py +2 -5
  13. dateutilx-0.3.0/dateutilx/utils/__init__.py +10 -0
  14. dateutilx-0.2.0/src/utils.py → dateutilx-0.3.0/dateutilx/utils/funcs.py +7 -9
  15. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx/utils}/weekday.py +6 -7
  16. {dateutilx-0.2.0 → dateutilx-0.3.0}/pyproject.toml +3 -3
  17. dateutilx-0.2.0/src/helper/__init__.py +0 -4
  18. dateutilx-0.2.0/src/helper/funcs.py +0 -5
  19. dateutilx-0.2.0/src/tz/_factories.py +0 -67
  20. dateutilx-0.2.0/src/tzwin.py +0 -4
  21. {dateutilx-0.2.0 → dateutilx-0.3.0}/.base_project/LICENSE +0 -0
  22. {dateutilx-0.2.0 → dateutilx-0.3.0}/LICENSE +0 -0
  23. {dateutilx-0.2.0 → dateutilx-0.3.0}/README.md +0 -0
  24. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/easter.py +0 -0
  25. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/errors.py +0 -0
  26. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/info.py +0 -0
  27. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/parser/__init__.py +0 -0
  28. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/parser/utils.py +0 -0
  29. {dateutilx-0.2.0/src → dateutilx-0.3.0/dateutilx}/tz/_common.py +0 -0
  30. {dateutilx-0.2.0/src/helper → dateutilx-0.3.0/dateutilx/utils}/constants.py +0 -0
@@ -17,9 +17,6 @@ venv/
17
17
  .hypothesis/
18
18
  .coverage
19
19
 
20
- # Autogenerated version information
21
- src/dateutilx/_version.py
22
-
23
20
  # Sphinx documentation
24
21
  docs/_build/
25
22
  docs/docs_venv
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dateutilx
3
- Version: 0.2.0
3
+ Version: 0.3.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/
@@ -13,7 +13,7 @@ def __getattr__(name):
13
13
 
14
14
  if name in __all__:
15
15
  return importlib.import_module(f".{name}", __name__)
16
- raise AttributeError(f"module {__name__!r} has not attribute {name!r}")
16
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
17
17
 
18
18
 
19
19
  def __dir__():
@@ -39,8 +39,8 @@ from decimal import Decimal
39
39
  from io import StringIO
40
40
  from warnings import warn
41
41
 
42
- from src import relativedelta, tz
43
- from src.errors import ParserError, UnknownTimezoneWarning
42
+ from dateutilx import relativedelta, tz
43
+ from dateutilx.errors import ParserError, UnknownTimezoneWarning
44
44
 
45
45
  from .utils import standard_dt_parser
46
46
 
@@ -562,8 +562,8 @@ class Parser:
562
562
  .. doctest::
563
563
  :options: +NORMALIZE_WHITESPACE
564
564
 
565
- >>> from src.parser import parse
566
- >>> from src.tz import gettz
565
+ >>> from dateutilx.parser import parse
566
+ >>> from dateutilx.tz import gettz
567
567
  >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
568
568
  >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
569
569
  datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
@@ -667,7 +667,7 @@ class Parser:
667
667
 
668
668
  .. doctest::
669
669
 
670
- >>> from src.parser import parse
670
+ >>> from dateutilx.parser import parse
671
671
  >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
672
672
  (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
673
673
 
@@ -1132,9 +1132,9 @@ class Parser:
1132
1132
  if isinstance(tzdata, datetime.tzinfo) or tzdata is None:
1133
1133
  tzinfo = tzdata
1134
1134
  elif isinstance(tzdata, str):
1135
- tzinfo = tz.tzstr(tzdata)
1135
+ tzinfo = tz.TzStr(tzdata)
1136
1136
  elif isinstance(tzdata, int):
1137
- tzinfo = tz.tzoffset(tzname, tzdata)
1137
+ tzinfo = tz.TzOffset(tzname, tzdata)
1138
1138
  else:
1139
1139
  raise TypeError("Offset must be tzinfo subclass, tz string, or int offset.")
1140
1140
  return tzinfo
@@ -1146,7 +1146,7 @@ class Parser:
1146
1146
  aware = self._assign_tzname(aware, res.tzname)
1147
1147
 
1148
1148
  elif res.tzname and res.tzname in time.tzname:
1149
- aware = naive.replace(tzinfo=tz.tzlocal())
1149
+ aware = naive.replace(tzinfo=tz.TzLocal())
1150
1150
 
1151
1151
  # Handle ambiguous local datetime
1152
1152
  aware = self._assign_tzname(aware, res.tzname)
@@ -1159,7 +1159,7 @@ class Parser:
1159
1159
  aware = naive.replace(tzinfo=tz.UTC)
1160
1160
 
1161
1161
  elif res.tzoffset:
1162
- aware = naive.replace(tzinfo=tz.tzoffset(res.tzname, res.tzoffset))
1162
+ aware = naive.replace(tzinfo=tz.TzOffset(res.tzname, res.tzoffset))
1163
1163
 
1164
1164
  elif not res.tzname:
1165
1165
  # i.e. no timezone information was found.
@@ -1268,8 +1268,8 @@ def parse(timestr, parser_info=None, **kwargs):
1268
1268
  .. doctest::
1269
1269
  :options: +NORMALIZE_WHITESPACE
1270
1270
 
1271
- >>> from src.parser import parse
1272
- >>> from src.tz import gettz
1271
+ >>> from dateutilx.parser import parse
1272
+ >>> from dateutilx.tz import gettz
1273
1273
  >>> tzinfos = {"BRST": -7200, "CST": gettz("America/Chicago")}
1274
1274
  >>> parse("2012-01-19 17:21:00 BRST", tzinfos=tzinfos)
1275
1275
  datetime.datetime(2012, 1, 19, 17, 21, tzinfo=tzoffset(u'BRST', -7200))
@@ -1305,7 +1305,7 @@ def parse(timestr, parser_info=None, **kwargs):
1305
1305
 
1306
1306
  .. doctest::
1307
1307
 
1308
- >>> from src.parser import parse
1308
+ >>> from dateutilx.parser import parse
1309
1309
  >>> parse("Today is January 1, 2047 at 8:21:00AM", fuzzy_with_tokens=True)
1310
1310
  (datetime.datetime(2047, 1, 1, 8, 21), (u'Today is ', u' ', u'at '))
1311
1311
 
@@ -12,7 +12,7 @@ import datetime as dt
12
12
  import re
13
13
  from functools import wraps
14
14
 
15
- from src.tz import UTC, TzOffset
15
+ from dateutilx.tz import UTC, TzOffset
16
16
 
17
17
  __all__ = ["isoparse", "isoparser"]
18
18
 
@@ -107,10 +107,10 @@ class IsoParser:
107
107
  - `±HHMM`
108
108
  - `±HH`
109
109
 
110
- Offsets will be represented as :class:`dateutilx.tz.tzoffset` objects,
110
+ Offsets will be represented as :class:`dateutilx.tz.TzOffset` objects,
111
111
  with the exception of UTC, which will be represented as
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`.
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`.
114
114
 
115
115
  :param dt_str:
116
116
  A string or stream containing only an ISO-8601 datetime string
@@ -188,11 +188,11 @@ class IsoParser:
188
188
  A string representing an ISO time zone offset
189
189
 
190
190
  :param zero_as_utc:
191
- Whether to return :class:`dateutilx.tz.tzutc` for zero-offset zones
191
+ Whether to return :class:`dateutilx.tz.TzUTC` for zero-offset zones
192
192
 
193
193
  :return:
194
- Returns :class:`dateutilx.tz.tzoffset` for offsets and
195
- :class:`dateutilx.tz.tzutc` for ``Z`` and (if ``zero_as_utc`` is
194
+ Returns :class:`dateutilx.tz.TzOffset` for offsets and
195
+ :class:`dateutilx.tz.TzUTC` for ``Z`` and (if ``zero_as_utc`` is
196
196
  specified) offsets equivalent to UTC.
197
197
  """
198
198
  return self._parse_tzstr(tzstr, zero_as_utc=zero_as_utc)
@@ -3,18 +3,13 @@ import datetime as dt
3
3
  import operator
4
4
  from math import copysign
5
5
  from types import NotImplementedType
6
- from typing import Self, TypeAlias
6
+ from typing import Self
7
7
  from warnings import warn
8
8
 
9
- from src.weekday import Weekday, weekdays
9
+ from dateutilx.utils import Weekday, weekdays
10
10
 
11
- MO, TU, WE, TH, FR, SA, SU = weekdays
12
-
13
- __all__ = ["RelativeDelta", "MO", "TU", "WE", "TH", "FR", "SA", "SU"]
14
- Number: TypeAlias = float | int
15
-
16
- REL_FIELDS = "years", "months", "days", "hours", "minutes", "seconds", "microseconds"
17
- ABS_FIELDS = "year", "month", "day", "hour", "minute", "second", "microsecond"
11
+ __all__ = ["RelativeDelta"]
12
+ Number = float | int
18
13
 
19
14
 
20
15
  class RelativeDelta:
@@ -91,7 +86,7 @@ class RelativeDelta:
91
86
  For example
92
87
 
93
88
  >>> from datetime import datetime
94
- >>> from src.relativedelta import RelativeDelta, MO
89
+ >>> from dateutilx.relativedelta import RelativeDelta, MO
95
90
  >>> dt_ = datetime(2018, 4, 9, 13, 37, 0)
96
91
  >>> delta = RelativeDelta(hours=25, day=1, weekday=MO(1))
97
92
  >>> dt_ + delta
@@ -177,7 +172,7 @@ class RelativeDelta:
177
172
 
178
173
  # Remove the year/month delta so the timedelta is just well-defined
179
174
  # time units (seconds, days and microseconds)
180
- dtm = self + dt2
175
+ dtm = self.__radd__(dt2)
181
176
 
182
177
  # If we've overshot our target, make an adjustment
183
178
  if dt1 < dt2:
@@ -190,7 +185,7 @@ class RelativeDelta:
190
185
  while compare(dt1, dtm):
191
186
  months += increment
192
187
  self._set_months(months)
193
- dtm = self + dt2
188
+ dtm = self.__radd__(dt2)
194
189
 
195
190
  # Get the timedelta between the "months-adjusted" date and dt1
196
191
  delta = dt1 - dtm
@@ -227,15 +222,37 @@ class RelativeDelta:
227
222
  resolved = dt.date(2001, 1, 1) + dt.timedelta(days=yday - 1)
228
223
  self.day, self.month = resolved.day, resolved.month
229
224
 
225
+ def _absolutes(self) -> dict[str, Number]:
226
+ return {
227
+ "year": self.year,
228
+ "month": self.month,
229
+ "day": self.day,
230
+ "hour": self.hour,
231
+ "minute": self.minute,
232
+ "second": self.second,
233
+ "microsecond": self.microsecond,
234
+ }
235
+
236
+ def _relatives(self) -> dict[str, Number]:
237
+ return {
238
+ "years": self.years,
239
+ "months": self.months,
240
+ "days": self.days,
241
+ "hours": self.hours,
242
+ "minutes": self.minutes,
243
+ "seconds": self.seconds,
244
+ "microseconds": self.microseconds,
245
+ }
246
+
230
247
  def _reset(self):
231
248
  self.leapdays = 0
232
249
  self.weekday = None
233
250
  self._has_time = 0
234
251
 
235
- for attr in REL_FIELDS:
252
+ for attr in ("years", "months", "days", "hours", "minutes", "seconds", "microseconds"):
236
253
  setattr(self, attr, 0)
237
254
 
238
- for attr in ABS_FIELDS:
255
+ for attr in ("year", "month", "day", "hour", "minute", "second", "microsecond"):
239
256
  setattr(self, attr, None)
240
257
 
241
258
  def _fix(self):
@@ -295,8 +312,8 @@ class RelativeDelta:
295
312
 
296
313
  def _make_relativedelta(self, **kwargs) -> Self:
297
314
  """Factory method to create a new RelativeDelta with given overrides."""
298
- rel_fields = {field: kwargs.get(field, getattr(self, field)) for field in REL_FIELDS}
299
- abs_fields = {field: kwargs.get(field, getattr(self, field)) for field in ABS_FIELDS}
315
+ rel_fields = {field: kwargs.get(field, val) for field, val in self._relatives().items()}
316
+ abs_fields = {field: kwargs.get(field, val) for field, val in self._absolutes().items()}
300
317
  return self.__class__(
301
318
  **rel_fields,
302
319
  **abs_fields,
@@ -337,8 +354,8 @@ class RelativeDelta:
337
354
  def __add__(self, other):
338
355
  if isinstance(other, RelativeDelta):
339
356
  kwargs = {
340
- field: getattr(self, field) if getattr(other, field) is None else getattr(other, field)
341
- for field in ABS_FIELDS
357
+ field: val if getattr(other, field) is None else getattr(other, field)
358
+ for field, val in self._absolutes().items()
342
359
  }
343
360
  return self._make_relativedelta(
344
361
  years=other.years + self.years,
@@ -407,10 +424,7 @@ class RelativeDelta:
407
424
  if not isinstance(other, RelativeDelta):
408
425
  return NotImplemented # In case the other object defines __rsub__
409
426
 
410
- kwargs = {
411
- field: getattr(other, field) if getattr(self, field) is None else getattr(self, field)
412
- for field in ABS_FIELDS
413
- }
427
+ kwargs = {field: getattr(other, field) if val is None else val for field, val in self._absolutes().items()}
414
428
  return self._make_relativedelta(
415
429
  years=self.years - other.years,
416
430
  months=self.months - other.months,
@@ -424,25 +438,28 @@ class RelativeDelta:
424
438
  )
425
439
 
426
440
  def __abs__(self):
427
- fields = {field: abs(getattr(self, field)) for field in REL_FIELDS}
441
+ fields = {field: abs(val) for field, val in self._relatives().items()}
428
442
  return self._make_relativedelta(**fields)
429
443
 
430
444
  def __neg__(self):
431
- fields = {field: -getattr(self, field) for field in REL_FIELDS}
445
+ fields = {field: -val for field, val in self._relatives().items()}
432
446
  return self._make_relativedelta(**fields)
433
447
 
434
- def __bool__(self):
435
- s_fields = (getattr(self, field) for field in REL_FIELDS)
436
- attribs = (getattr(self, field) for field in ABS_FIELDS)
437
- return any((*s_fields, self.leapdays, self.weekday, *(x is not None for x in attribs)))
448
+ def __bool__(self) -> bool:
449
+ return (
450
+ any(value for value in self._relatives().values())
451
+ or bool(self.leapdays)
452
+ or bool(self.weekday)
453
+ or any(value is not None for value in self._absolutes().values())
454
+ )
438
455
 
439
- def __mul__(self, other) -> NotImplementedType | Self:
456
+ def __mul__(self, other) -> Self | NotImplementedType:
440
457
  try:
441
458
  f = float(other)
442
459
  except TypeError:
443
460
  return NotImplemented
444
461
 
445
- fields = {field: int(getattr(self, field) * f) for field in REL_FIELDS}
462
+ fields = {field: int(val * f) for field, val in self._relatives().items()}
446
463
  return self._make_relativedelta(**fields)
447
464
 
448
465
  __rmul__ = __mul__
@@ -463,8 +480,10 @@ class RelativeDelta:
463
480
  return False
464
481
 
465
482
  return (
466
- all((getattr(self, field) == getattr(other, field) for field in REL_FIELDS + ABS_FIELDS))
483
+ all((a == b for a, b in zip(self._relatives().values(), other._relatives().values())))
484
+ and all((a == b for a, b in zip(self._absolutes().values(), other._absolutes().values())))
467
485
  and self.leapdays == other.leapdays
486
+ and self.weekday == other.weekday
468
487
  )
469
488
 
470
489
  def __hash__(self):
@@ -483,7 +502,7 @@ class RelativeDelta:
483
502
  return NotImplemented
484
503
  return not self.__eq__(other)
485
504
 
486
- def __div__(self, other) -> NotImplementedType | Self:
505
+ def __truediv__(self, other) -> NotImplementedType | Self:
487
506
  try:
488
507
  reciprocal = 1 / float(other)
489
508
  except TypeError:
@@ -491,8 +510,6 @@ class RelativeDelta:
491
510
 
492
511
  return self.__mul__(reciprocal)
493
512
 
494
- __truediv__ = __div__
495
-
496
513
  def __repr__(self):
497
514
  _tmp_list = []
498
515
  for attr in ["years", "months", "days", "leapdays", "hours", "minutes", "seconds", "microseconds"]:
@@ -6,7 +6,6 @@ the recurrence rules documented in the
6
6
  including support for caching of results.
7
7
  """
8
8
 
9
- import _thread
10
9
  import calendar
11
10
  import contextlib
12
11
  import datetime as dt
@@ -15,14 +14,14 @@ import itertools
15
14
  import operator
16
15
  import re
17
16
  import sys
17
+ import threading
18
18
  from functools import wraps
19
19
  from itertools import product
20
20
  from math import gcd
21
21
  from typing import Any
22
22
  from warnings import warn
23
23
 
24
- from .helper import Day, Frequency
25
- from .weekday import Weekday, weekdays
24
+ from dateutilx.utils import Day, Frequency, Weekday, weekdays
26
25
 
27
26
  # fmt: off
28
27
  __all__ = [
@@ -79,7 +78,7 @@ class RruleBase:
79
78
  def __init__(self, cache=False):
80
79
  if cache:
81
80
  self._cache = []
82
- self._cache_lock = _thread.allocate_lock()
81
+ self._cache_lock = threading.Lock()
83
82
  self._invalidate_cache()
84
83
  else:
85
84
  self._cache = None
@@ -270,7 +269,7 @@ class Rrule(RruleBase):
270
269
  This can lead to possibly surprising behavior when, for example, the
271
270
  start date occurs at the end of the month:
272
271
 
273
- >>> from src.rrule import rrule, MONTHLY
272
+ >>> from dateutilx.rrule import rrule, MONTHLY
274
273
  >>> from datetime import datetime
275
274
  >>> start_date = datetime(2014, 12, 31)
276
275
  >>> list(rrule(freq=MONTHLY, count=4, dtstart=start_date))
@@ -498,7 +497,7 @@ class Rrule(RruleBase):
498
497
  self._byeaster = None
499
498
  if byeaster is not None:
500
499
  if not easter:
501
- from src import easter
500
+ from dateutilx import easter
502
501
  if isinstance(byeaster, int):
503
502
  self._byeaster = (byeaster,)
504
503
  else:
@@ -1001,7 +1000,7 @@ class Rrule(RruleBase):
1001
1000
 
1002
1001
  return cset
1003
1002
 
1004
- def __mod_distance(self, value, byxxx, base):
1003
+ def __mod_distance(self, value, byxxx, base) -> tuple[int, int]:
1005
1004
  """
1006
1005
  Calculates the next value in a sequence where the `FREQ` parameter is
1007
1006
  specified along with a `BYXXX` parameter at the same "level"
@@ -1032,7 +1031,7 @@ class Rrule(RruleBase):
1032
1031
  accumulator += div
1033
1032
  if value in byxxx:
1034
1033
  return accumulator, value
1035
- return None
1034
+ raise ValueError("No valid values found")
1036
1035
 
1037
1036
 
1038
1037
  # pylint: disable=E0203
@@ -1403,7 +1402,7 @@ class _RRuleStr:
1403
1402
  def _handle_until(rrkwargs, name, value, **kwargs):
1404
1403
  global parser
1405
1404
  if not parser:
1406
- from src import parser
1405
+ from dateutilx import parser
1407
1406
  try:
1408
1407
  rrkwargs["until"] = parser.parse(value, ignoretz=kwargs.get("ignoretz"), tzinfos=kwargs.get("tzinfos"))
1409
1408
  except ValueError as e:
@@ -1465,7 +1464,7 @@ class _RRuleStr:
1465
1464
  def _parse_date_value(self, date_value, parms, rule_tzids, *, ignoretz, tzids, tzinfos):
1466
1465
  global parser
1467
1466
  if not parser:
1468
- from src import parser
1467
+ from dateutilx import parser
1469
1468
 
1470
1469
  datevals = []
1471
1470
  value_found = False
@@ -1597,7 +1596,7 @@ class _RRuleStr:
1597
1596
 
1598
1597
  if forceset or len(rrulevals) > 1 or rdatevals or exrulevals or exdatevals:
1599
1598
  if not parser and (rdatevals or exdatevals):
1600
- from src import parser
1599
+ from dateutilx import parser
1601
1600
  rset = RruleSet(cache=cache)
1602
1601
  for value in rrulevals:
1603
1602
  rset.rrule(self._parse_rfc_rrule(value, dtstart=dtstart, ignoretz=ignoretz, tzinfos=tzinfos))
@@ -1,5 +1,4 @@
1
1
  from .tz import (
2
- UTC,
3
2
  TzFile,
4
3
  TzIcal,
5
4
  TzLocal,
@@ -12,25 +11,17 @@ from .tz import (
12
11
  gettz,
13
12
  resolve_imaginary,
14
13
  )
15
- from .win import TzWin, TzWinLocal, tzwin, tzwinlocal
16
14
 
17
15
  # fmt: off
18
16
  __all__ = ["TzFile", "TzIcal", "TzLocal", "TzOffset", "TzRange", "TzStr", "TzUTC",
19
- "tzutc", "gettz", "datetime_ambiguous", "datetime_exists",
17
+ "gettz", "datetime_ambiguous", "datetime_exists",
20
18
  "resolve_imaginary", "UTC", "DeprecatedTzFormatWarning",
21
- "TzWin", "TzWinLocal"]
19
+ ]
22
20
  # fmt: on
23
21
 
24
22
 
25
- class DeprecatedTzFormatWarning(Warning):
26
- """Warning raised when time zones are parsed from deprecated formats."""
23
+ UTC = TzUTC()
27
24
 
28
25
 
29
- # Alias
30
- tzstr = TzStr
31
- tzfile = TzFile
32
- tzical = TzIcal
33
- tzlocal = TzLocal
34
- tzoffset = TzOffset
35
- tzrange = TzRange
36
- tzutc = TzUTC
26
+ class DeprecatedTzFormatWarning(Warning):
27
+ """Warning raised when time zones are parsed from deprecated formats."""
@@ -0,0 +1,61 @@
1
+ import weakref
2
+ from collections import OrderedDict
3
+ from datetime import timedelta
4
+
5
+
6
+ class _TzSingleton(type):
7
+ def __init__(cls, *args, **kwargs):
8
+ cls.__instance = None
9
+ super().__init__(*args, **kwargs)
10
+
11
+ def __call__(cls):
12
+ if cls.__instance is None:
13
+ cls.__instance = super().__call__()
14
+ return cls.__instance
15
+
16
+
17
+ class _TzFactory(type):
18
+ def instance(cls, *args, **kwargs):
19
+ """Alternate constructor that returns a fresh instance"""
20
+ return type.__call__(cls, *args, **kwargs)
21
+
22
+
23
+ class _TzCachedFactory(_TzFactory):
24
+ """Factory with a weak identity cache plus a small strong LRU cache
25
+ to keep the most recently used instances alive."""
26
+
27
+ _strong_cache_size = 8
28
+
29
+ def __init__(cls, *args, **kwargs):
30
+ cls._instances = weakref.WeakValueDictionary()
31
+ cls._strong_cache = OrderedDict()
32
+ super().__init__(*args, **kwargs)
33
+
34
+ def _cache_key(cls, *args, **kwargs):
35
+ """Subclasses define how constructor args map to a cache key."""
36
+ raise NotImplementedError
37
+
38
+ def __call__(cls, *args, **kwargs):
39
+ key = cls._cache_key(*args, **kwargs)
40
+
41
+ instance = cls._instances.get(key)
42
+ if instance is None:
43
+ instance = cls._instances.setdefault(key, cls.instance(*args, **kwargs))
44
+
45
+ cls._strong_cache[key] = cls._strong_cache.pop(key, instance)
46
+ if len(cls._strong_cache) > cls._strong_cache_size:
47
+ cls._strong_cache.popitem(last=False)
48
+
49
+ return instance
50
+
51
+
52
+ class _TzOffsetFactory(_TzCachedFactory):
53
+ def _cache_key(cls, name, offset):
54
+ if isinstance(offset, timedelta):
55
+ offset = offset.total_seconds()
56
+ return name, offset
57
+
58
+
59
+ class _TzStrFactory(_TzCachedFactory):
60
+ def _cache_key(cls, s, posix_offset=False):
61
+ return s, posix_offset
@@ -69,7 +69,7 @@ class TzRes:
69
69
  """
70
70
  Load a timezone name from a DLL offset (integer).
71
71
 
72
- >>> from src.tzwin import TzRes
72
+ >>> from dateutilx.tzwin import TzRes
73
73
  >>> tzr = TzRes()
74
74
  >>> print(tzr.load_name(112))
75
75
  'Eastern Standard Time'
@@ -93,7 +93,7 @@ class TzRes:
93
93
  Parse strings as returned from the Windows registry into the time zone
94
94
  name as defined in the registry.
95
95
 
96
- >>> from src.tzwin import TzRes
96
+ >>> from dateutilx.tzwin import TzRes
97
97
  >>> tzr = TzRes()
98
98
  >>> print(tzr.name_from_string('@tzres.dll,-251'))
99
99
  'Dateline Daylight Time'
@@ -255,16 +255,16 @@ class TzWinLocal(TzWinBase):
255
255
  """
256
256
  Class representing the local time zone information in the Windows registry
257
257
 
258
- While :class:`dateutilx.tz.tzlocal` makes system calls (via the :mod:`time`
258
+ While :class:`dateutilx.tz.TzLocal` makes system calls (via the :mod:`time`
259
259
  module) to retrieve time zone information, ``tzwinlocal`` retrieves the
260
260
  rules directly from the Windows registry and creates an object like
261
261
  :class:`dateutilx.tz.tzwin`.
262
262
 
263
263
  Because Windows does not have an equivalent of :func:`time.tzset`, on
264
- Windows, :class:`dateutilx.tz.tzlocal` instances will always reflect the
264
+ Windows, :class:`dateutilx.tz.TzLocal` instances will always reflect the
265
265
  time zone settings *at the time that the process was started*, meaning
266
266
  changes to the machine's time zone settings during the run of a program
267
- on Windows will **not** be reflected by :class:`dateutilx.tz.tzlocal`.
267
+ on Windows will **not** be reflected by :class:`dateutilx.tz.TzLocal`.
268
268
  Because ``tzwinlocal`` reads the registry directly, it is unaffected by
269
269
  this issue.
270
270
  """
@@ -7,19 +7,19 @@ from relative deltas), local machine timezone, fixed offset timezone, and UTC
7
7
  timezone.
8
8
  """
9
9
 
10
- import _thread
11
10
  import bisect
12
11
  import contextlib
13
12
  import datetime
14
13
  import os
15
14
  import struct
15
+ import threading
16
16
  import time
17
17
  import weakref
18
18
  from collections import OrderedDict
19
19
  from dataclasses import dataclass
20
20
  from typing import Any
21
21
 
22
- from src.helper import is_windows_os
22
+ from dateutilx.utils import Day, is_windows_os, weekdays
23
23
 
24
24
  from ._common import TzRangeBase, _TzInfo, _validate_fromutc_inputs
25
25
  from ._factories import _TzOffsetFactory, _TzSingleton, _TzStrFactory
@@ -29,6 +29,7 @@ rrule = None # pylint: disable=c0103
29
29
  ZERO = datetime.timedelta(0)
30
30
  EPOCH = datetime.datetime(1970, 1, 1, 0, 0)
31
31
  EPOCHORDINAL = EPOCH.toordinal()
32
+ SU = weekdays[Day.SU]
32
33
 
33
34
 
34
35
  class TzUTC(datetime.tzinfo, metaclass=_TzSingleton):
@@ -40,7 +41,7 @@ class TzUTC(datetime.tzinfo, metaclass=_TzSingleton):
40
41
  .. doctest::
41
42
 
42
43
  >>> import datetime as dt
43
- >>> from src.tz import *
44
+ >>> from dateutilx.tz import *
44
45
 
45
46
  >>> dt.datetime.now()
46
47
  datetime.datetime(2003, 9, 27, 9, 40, 1, 521290)
@@ -57,7 +58,7 @@ class TzUTC(datetime.tzinfo, metaclass=_TzSingleton):
57
58
 
58
59
  .. doctest::
59
60
 
60
- >>> from src.tz import tzutc, UTC
61
+ >>> from dateutilx.tz import tzutc, UTC
61
62
  >>> tzutc() is tzutc()
62
63
  True
63
64
  >>> tzutc() is UTC
@@ -111,9 +112,6 @@ class TzUTC(datetime.tzinfo, metaclass=_TzSingleton):
111
112
  return f"{self.__name__}()"
112
113
 
113
114
 
114
- #: Convenience constant providing a :class:`tzutc()` instance
115
- #:
116
- #: .. versionadded:: 2.7.0
117
115
  UTC = TzUTC()
118
116
 
119
117
 
@@ -242,7 +240,7 @@ class TzLocal(_TzInfo):
242
240
  # The code above yields the following result:
243
241
  #
244
242
  # >>> import tz, datetime
245
- # >>> t = tz.tzlocal()
243
+ # >>> t = tz.TzLocal()
246
244
  # >>> datetime.datetime(2003,2,15,23,tzinfo=t).tzname()
247
245
  # 'BRDT'
248
246
  # >>> datetime.datetime(2003,2,16,0,tzinfo=t).tzname()
@@ -344,9 +342,6 @@ class _TzFile:
344
342
  ttinfo_first: Any | None = None
345
343
 
346
344
 
347
- _tzfile = _TzFile
348
-
349
-
350
345
  class TzFile(_TzInfo):
351
346
  """
352
347
  This is a ``tzinfo`` subclass that allows one to use the ``tzfile(5)``
@@ -384,7 +379,7 @@ class TzFile(_TzInfo):
384
379
 
385
380
  .. testsetup:: tzfile
386
381
 
387
- from src.tz import gettz
382
+ from dateutilx.tz import gettz
388
383
  from datetime import datetime
389
384
 
390
385
  .. doctest:: tzfile
@@ -446,7 +441,7 @@ class TzFile(_TzInfo):
446
441
  def _set_tzdata(self, tzobj):
447
442
  """Set the time zone data of this object from a _tzfile object"""
448
443
  # Copy the relevant attributes over as private attributes
449
- for attr in _tzfile.__slots__:
444
+ for attr in _TzFile.__slots__:
450
445
  setattr(self, f"_{attr}", getattr(tzobj, attr))
451
446
 
452
447
  def _read_tzfile(self, fileobj):
@@ -867,14 +862,14 @@ class TzRange(TzRangeBase):
867
862
 
868
863
  .. testsetup:: tzrange
869
864
 
870
- >>> from src.tz import tzrange, tzstr
865
+ >>> from dateutilx.tz import tzrange, tzstr
871
866
 
872
867
  .. doctest:: tzrange
873
868
 
874
869
  >>> tzstr('EST5EDT') == tzrange("EST", -18000, "EDT")
875
870
  True
876
871
 
877
- >>> from src.relativedelta import *
872
+ >>> from dateutilx.relativedelta import *
878
873
  >>> range1 = tzrange("EST", -18000, "EDT")
879
874
  >>> range2 = tzrange("EST", -18000, "EDT", -14400,
880
875
  ... RelativeDelta(hours=+2, month=4, day=1,
@@ -888,7 +883,8 @@ class TzRange(TzRangeBase):
888
883
 
889
884
  def __init__(self, stdabbr, stdoffset=None, dstabbr=None, dstoffset=None, *, start=None, end=None):
890
885
  # global relativedelta
891
- from src.relativedelta import SU, RelativeDelta
886
+ from dateutilx.relativedelta import RelativeDelta
887
+ from dateutilx.utils.weekday import SU
892
888
 
893
889
  self._std_abbr = stdabbr
894
890
  self._dst_abbr = dstabbr
@@ -1009,7 +1005,7 @@ class TzStr(TzRange, metaclass=_TzStrFactory):
1009
1005
 
1010
1006
  def __init__(self, s, posix_offset=False):
1011
1007
  global parser
1012
- from src.parser import _parser as parser
1008
+ from dateutilx.parser import _parser as parser
1013
1009
 
1014
1010
  self._s = s
1015
1011
 
@@ -1038,7 +1034,7 @@ class TzStr(TzRange, metaclass=_TzStrFactory):
1038
1034
  self.hasdst = bool(self._start_delta)
1039
1035
 
1040
1036
  def _delta(self, x, isend=0):
1041
- from src import relativedelta
1037
+ from dateutilx import relativedelta
1042
1038
 
1043
1039
  kwargs = {}
1044
1040
  if x.month is not None:
@@ -1058,11 +1054,11 @@ class TzStr(TzRange, metaclass=_TzStrFactory):
1058
1054
  if not isend:
1059
1055
  kwargs["month"] = 4
1060
1056
  kwargs["day"] = 1
1061
- kwargs["weekday"] = relativedelta.SU(+1)
1057
+ kwargs["weekday"] = SU(+1)
1062
1058
  else:
1063
1059
  kwargs["month"] = 10
1064
1060
  kwargs["day"] = 31
1065
- kwargs["weekday"] = relativedelta.SU(-1)
1061
+ kwargs["weekday"] = SU(-1)
1066
1062
 
1067
1063
  kwargs["seconds"] = x.time if x.time is not None else 7200 # Default is 2AM.
1068
1064
 
@@ -1094,7 +1090,7 @@ class _TzIcalVtz(_TzInfo):
1094
1090
  self._tzid = tzid
1095
1091
  self._comps = comps or []
1096
1092
  self._cache = OrderedDict()
1097
- self._cache_lock = _thread.allocate_lock()
1093
+ self._cache_lock = threading.Lock()
1098
1094
 
1099
1095
  def _find_comp(self, dt):
1100
1096
  if len(self._comps) == 1:
@@ -1176,7 +1172,7 @@ class TzIcal:
1176
1172
  def __init__(self, fileobj):
1177
1173
  global rrule
1178
1174
  if not rrule:
1179
- from src import rrule
1175
+ from dateutilx import rrule
1180
1176
 
1181
1177
  if isinstance(fileobj, str):
1182
1178
  self._s = fileobj
@@ -1436,7 +1432,7 @@ def __get_gettz():
1436
1432
  self.__instances = weakref.WeakValueDictionary()
1437
1433
  self.__strong_cache_size = 8
1438
1434
  self.__strong_cache = OrderedDict()
1439
- self._cache_lock = _thread.allocate_lock()
1435
+ self._cache_lock = threading.Lock()
1440
1436
 
1441
1437
  def __call__(self, name=None):
1442
1438
  with self._cache_lock:
@@ -1651,7 +1647,7 @@ def resolve_imaginary(dt):
1651
1647
 
1652
1648
  .. doctest::
1653
1649
 
1654
- >>> from src import tz
1650
+ >>> from dateutilx import tz
1655
1651
  >>> from datetime import datetime
1656
1652
  >>> NYC = tz.gettz('America/New_York')
1657
1653
  >>> print(tz.resolve_imaginary(datetime(2017, 3, 12, 2, 30, tzinfo=NYC)))
@@ -2,14 +2,11 @@
2
2
  Wrapper for _win.py to handle platform dependent import
3
3
  """
4
4
 
5
- from src.helper import is_windows_os
5
+ from dateutilx.utils import is_windows_os
6
6
 
7
7
  if is_windows_os():
8
8
  from ._win import TzRes, TzWin, TzWinLocal
9
9
  else:
10
10
  TzRes = TzWin = TzWinLocal = None
11
11
 
12
- # Alias
13
- tzres = TzRes
14
- tzwin = TzWin
15
- tzwinlocal = TzWinLocal
12
+ __all__ = ["TzRes", "TzWin", "TzWinLocal"]
@@ -0,0 +1,10 @@
1
+ from .constants import Day, Frequency, Month
2
+ from .funcs import default_tzinfo, is_windows_os, today, within_delta
3
+ from .weekday import Weekday, weekdays
4
+
5
+ # fmt: off
6
+ __all__ = ["Day", "Frequency", "Month",
7
+ "default_tzinfo", "is_windows_os", "today", "within_delta",
8
+ "Weekday", "weekdays"
9
+ ]
10
+ # fmt: on
@@ -1,11 +1,9 @@
1
- """
2
- This module offers general convenience and utility functions for dealing with
3
- datetimes.
1
+ import datetime as dt
2
+ import sys
4
3
 
5
- .. versionadded:: 2.7.0
6
- """
7
4
 
8
- import datetime as dt
5
+ def is_windows_os() -> bool:
6
+ return sys.platform.startswith("win")
9
7
 
10
8
 
11
9
  def today(tzinfo=None):
@@ -33,9 +31,9 @@ def default_tzinfo(dt, tzinfo):
33
31
 
34
32
  .. doctest::
35
33
 
36
- >>> from src.tz import tzoffset
37
- >>> from src.parser import parse
38
- >>> from src.utils import default_tzinfo
34
+ >>> from dateutilx.tz import tzoffset
35
+ >>> from dateutilx.parser import parse
36
+ >>> from dateutilx.utils import default_tzinfo
39
37
  >>> dflt_tz = tzoffset("EST", -18000)
40
38
  >>> print(default_tzinfo(parse('2014-01-01 12:30 UTC'), dflt_tz))
41
39
  2014-01-01 12:30:00+00:00
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from src.helper import Day
3
+ from .constants import Day
4
4
 
5
5
 
6
6
  class NthWeekday:
@@ -22,6 +22,9 @@ class NthWeekday:
22
22
  except AttributeError:
23
23
  return False
24
24
 
25
+ def __hash__(self):
26
+ return hash((self.weekday, self.n))
27
+
25
28
  def __repr__(self) -> str:
26
29
  name = self.weekday.name
27
30
  return f"{name}({self.n:+d})" if self.n else name
@@ -30,13 +33,9 @@ class NthWeekday:
30
33
  # pylint: disable=r0903
31
34
  class Weekday(NthWeekday):
32
35
  def __init__(self, wkday: int, n: int | None = None):
36
+ if not 0 <= wkday <= 6:
37
+ raise ValueError(f"Weekday must be between 0 and 6, got {wkday}")
33
38
  super().__init__(wkday=Day(wkday), n=n)
34
39
 
35
- def __hash__(self):
36
- return hash((self.weekday, self.n))
37
-
38
40
 
39
41
  MO, TU, WE, TH, FR, SA, SU = weekdays = tuple(Weekday(x) for x in range(7))
40
-
41
- # Alias
42
- weekday = Weekday
@@ -43,12 +43,12 @@ Source = "https://github.com/rsb-23/dateutilx"
43
43
  [tool.hatch.version]
44
44
  source = "vcs"
45
45
  [tool.hatch.build.targets.sdist]
46
- include = ["src", "LICENSE", "README.md", "pyproject.toml"]
46
+ include = ["dateutilx", "LICENSE", "README.md", "pyproject.toml"]
47
47
  [tool.hatch.build.targets.wheel]
48
- packages = ["src"]
48
+ packages = ["dateutilx"]
49
49
 
50
50
  [tool.coverage.run]
51
- source = ["src"]
51
+ source = ["dateutilx"]
52
52
  [tool.coverage.report]
53
53
  fail_under = 85.0
54
54
  skip_covered = true
@@ -1,4 +0,0 @@
1
- from .constants import Day, Frequency, Month
2
- from .funcs import is_windows_os
3
-
4
- __all__ = ["Day", "Frequency", "Month", "is_windows_os"]
@@ -1,5 +0,0 @@
1
- import sys
2
-
3
-
4
- def is_windows_os() -> bool:
5
- return sys.platform.startswith("win")
@@ -1,67 +0,0 @@
1
- import weakref
2
- from collections import OrderedDict
3
- from datetime import timedelta
4
-
5
-
6
- class _TzSingleton(type):
7
- def __init__(cls, *args, **kwargs):
8
- cls.__instance = None
9
- super().__init__(*args, **kwargs)
10
-
11
- def __call__(cls):
12
- if cls.__instance is None:
13
- cls.__instance = super().__call__()
14
- return cls.__instance
15
-
16
-
17
- class _TzFactory(type):
18
- def instance(cls, *args, **kwargs):
19
- """Alternate constructor that returns a fresh instance"""
20
- return type.__call__(cls, *args, **kwargs)
21
-
22
-
23
- class _TzOffsetFactory(_TzFactory):
24
- def __init__(cls, *args, **kwargs):
25
- cls.__instances = weakref.WeakValueDictionary()
26
- cls.__strong_cache = OrderedDict()
27
- cls.__strong_cache_size = 8
28
-
29
- def __call__(cls, name, offset):
30
- if isinstance(offset, timedelta):
31
- key = (name, offset.total_seconds())
32
- else:
33
- key = (name, offset)
34
-
35
- instance = cls.__instances.get(key, None)
36
- if instance is None:
37
- instance = cls.__instances.setdefault(key, cls.instance(name, offset))
38
-
39
- cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
40
-
41
- # Remove an item if the strong cache is overpopulated
42
- if len(cls.__strong_cache) > cls.__strong_cache_size:
43
- cls.__strong_cache.popitem(last=False)
44
-
45
- return instance
46
-
47
-
48
- class _TzStrFactory(_TzFactory):
49
- def __init__(cls, *args, **kwargs):
50
- cls.__instances = weakref.WeakValueDictionary()
51
- cls.__strong_cache = OrderedDict()
52
- cls.__strong_cache_size = 8
53
-
54
- def __call__(cls, s, posix_offset=False):
55
- key = (s, posix_offset)
56
- instance = cls.__instances.get(key, None)
57
-
58
- if instance is None:
59
- instance = cls.__instances.setdefault(key, cls.instance(s, posix_offset))
60
-
61
- cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
62
-
63
- # Remove an item if the strong cache is overpopulated
64
- if len(cls.__strong_cache) > cls.__strong_cache_size:
65
- cls.__strong_cache.popitem(last=False)
66
-
67
- return instance
@@ -1,4 +0,0 @@
1
- # tzwin has moved to dateutilx.tz.win
2
- from .tz.win import TzRes, TzWin, TzWinLocal, tzres, tzwin, tzwinlocal # noqa
3
-
4
- __all__ = ["TzRes", "TzWin", "TzWinLocal", "tzres", "tzwin", "tzwinlocal"]
File without changes
File without changes