dycw-utilities 0.131.12__py3-none-any.whl → 0.131.14__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.
- {dycw_utilities-0.131.12.dist-info → dycw_utilities-0.131.14.dist-info}/METADATA +1 -1
- {dycw_utilities-0.131.12.dist-info → dycw_utilities-0.131.14.dist-info}/RECORD +14 -13
- utilities/__init__.py +1 -1
- utilities/atools.py +7 -9
- utilities/cachetools.py +8 -10
- utilities/hypothesis.py +0 -217
- utilities/orjson.py +1 -1
- utilities/period.py +86 -256
- utilities/typed_settings.py +61 -0
- utilities/tzdata.py +1 -53
- utilities/whenever2.py +4 -3
- utilities/zoneinfo.py +2 -2
- {dycw_utilities-0.131.12.dist-info → dycw_utilities-0.131.14.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.131.12.dist-info → dycw_utilities-0.131.14.dist-info}/licenses/LICENSE +0 -0
@@ -1,10 +1,10 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=NYq6mGcB8IGMU4RPF-m9jjQGEJiGNJEcMMoXW81ZW3E,61
|
2
2
|
utilities/aiolimiter.py,sha256=mD0wEiqMgwpty4XTbawFpnkkmJS6R4JRsVXFUaoitSU,628
|
3
3
|
utilities/altair.py,sha256=HeZBVUocjkrTNwwKrClppsIqgNFF-ykv05HfZSoHYno,9104
|
4
4
|
utilities/asyncio.py,sha256=yfKvAIDCRrWdyQMVZMo4DJQx4nVrXoAcqwhNuF95Ryo,38186
|
5
5
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
6
|
-
utilities/atools.py,sha256
|
7
|
-
utilities/cachetools.py,sha256=
|
6
|
+
utilities/atools.py,sha256=-bFGIrwYMFR7xl39j02DZMsO_u5x5_Ph7bRlBUFVYyw,1048
|
7
|
+
utilities/cachetools.py,sha256=uBtEv4hD-TuCPX_cQy1lOpLF-QqfwnYGSf0o4Soqydc,2826
|
8
8
|
utilities/click.py,sha256=8gRYeyu9KQ3uim0UpC8VnFnOOKD3DyGwMJ7k0Qns1lM,14659
|
9
9
|
utilities/concurrent.py,sha256=s2scTEd2AhXVTW4hpASU2qxV_DiVLALfms55cCQzCvM,2886
|
10
10
|
utilities/contextlib.py,sha256=lpaLJBy3X0UGLWjM98jkQZZq8so4fRmoK-Bheq0uOW4,1027
|
@@ -24,7 +24,7 @@ utilities/getpass.py,sha256=DfN5UgMAtFCqS3dSfFHUfqIMZX2shXvwphOz_6J6f6A,103
|
|
24
24
|
utilities/git.py,sha256=oi7-_l5e9haSANSCvQw25ufYGoNahuUPHAZ6114s3JQ,1191
|
25
25
|
utilities/hashlib.py,sha256=SVTgtguur0P4elppvzOBbLEjVM3Pea0eWB61yg2ilxo,309
|
26
26
|
utilities/http.py,sha256=WcahTcKYRtZ04WXQoWt5EGCgFPcyHD3EJdlMfxvDt-0,946
|
27
|
-
utilities/hypothesis.py,sha256=
|
27
|
+
utilities/hypothesis.py,sha256=hEyuJoLH0c6So0P5OESvyANpfM49Ld1iYXC4bZvacm4,42603
|
28
28
|
utilities/importlib.py,sha256=mV1xT_O_zt_GnZZ36tl3xOmMaN_3jErDWY54fX39F6Y,429
|
29
29
|
utilities/inflect.py,sha256=DbqB5Q9FbRGJ1NbvEiZBirRMxCxgrz91zy5jCO9ZIs0,347
|
30
30
|
utilities/ipython.py,sha256=V2oMYHvEKvlNBzxDXdLvKi48oUq2SclRg5xasjaXStw,763
|
@@ -41,11 +41,11 @@ utilities/more_itertools.py,sha256=tBbjjKx8_Uv_TCjxhPwrGfAx_jRHtvLIZqXVWAsjzqA,8
|
|
41
41
|
utilities/numpy.py,sha256=Xn23sA2ZbVNqwUYEgNJD3XBYH6IbCri_WkHSNhg3NkY,26122
|
42
42
|
utilities/operator.py,sha256=0M2yZJ0PODH47ogFEnkGMBe_cfxwZR02T_92LZVZvHo,3715
|
43
43
|
utilities/optuna.py,sha256=loyJGWTzljgdJaoLhP09PT8Jz6o_pwBOwehY33lHkhw,1923
|
44
|
-
utilities/orjson.py,sha256=
|
44
|
+
utilities/orjson.py,sha256=lhBSO-QCm2g-uhUE0hKe-PVhAt1TKRRx0iZLSFPmew4,36970
|
45
45
|
utilities/os.py,sha256=D_FyyT-6TtqiN9KSS7c9g1fnUtgxmyMtzAjmYLkk46A,3587
|
46
46
|
utilities/parse.py,sha256=vsZ2jf_ceSI_Kta9titixufysJaVXh0Whjz1T4awJZw,18938
|
47
47
|
utilities/pathlib.py,sha256=PK41rf1c9Wqv7h8f5R7H3_Lhq_gQZTUJD5tu3gMHVaU,3247
|
48
|
-
utilities/period.py,sha256=
|
48
|
+
utilities/period.py,sha256=opqpBevBGSGXbA7NYfRJjtthi1JPxdMaZ7QV3xosnTc,4774
|
49
49
|
utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
|
50
50
|
utilities/platform.py,sha256=48IOKx1IC6ZJXWG-b56ZQptITcNFhWRjELW72o2dGTA,2398
|
51
51
|
utilities/polars.py,sha256=QlmUpYTqHNkcLnWOQh1TW22W2QyLzvifCvBcbsqhpdE,63272
|
@@ -80,18 +80,19 @@ utilities/text.py,sha256=ymBFlP_cA8OgNnZRVNs7FAh7OG8HxE6YkiLEMZv5g_A,11297
|
|
80
80
|
utilities/threading.py,sha256=GvBOp4CyhHfN90wGXZuA2VKe9fGzMaEa7oCl4f3nnPU,1009
|
81
81
|
utilities/timer.py,sha256=VeSl3ot8-f4D1d3HjjSsgKvjxHJGXd_sW4KcTExOR64,2475
|
82
82
|
utilities/traceback.py,sha256=cMXrCD59CROnezAU8VW67CxZ8Igc5QmaxlV8qrBvNMs,8504
|
83
|
+
utilities/typed_settings.py,sha256=zUA0_CmVJT5rwrm3e-dZO83OdPXEel4NfVK24NAD5Vk,1779
|
83
84
|
utilities/types.py,sha256=CHQke10ETEpypxppYVhWp1G68S6mvifalrRLolYBcCg,19506
|
84
85
|
utilities/typing.py,sha256=VuGuztLSkTicxgVwI5wrVOTcY70OlzwsTU7LcFVjGlY,14169
|
85
|
-
utilities/tzdata.py,sha256=
|
86
|
+
utilities/tzdata.py,sha256=fgNVj66yUbCSI_-vrRVzSD3gtf-L_8IEJEPjP_Jel5Y,266
|
86
87
|
utilities/tzlocal.py,sha256=xbBBzVIUKMk8AkhuIp1qxGRNBioIa5I09dpeoBnIOOU,662
|
87
88
|
utilities/uuid.py,sha256=jJTFxz-CWgltqNuzmythB7iEQ-Q1mCwPevUfKthZT3c,611
|
88
89
|
utilities/version.py,sha256=ufhJMmI6KPs1-3wBI71aj5wCukd3sP_m11usLe88DNA,5117
|
89
90
|
utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
90
91
|
utilities/whenever.py,sha256=2NQ-0SnLNW2kFpefP9dVE8H0RbaeusXYLPmv282Jpto,16755
|
91
|
-
utilities/whenever2.py,sha256=
|
92
|
+
utilities/whenever2.py,sha256=iFVL4CjuIOpzsDU6li5smHnDEqam30-FtTgXWeHuWiE,7510
|
92
93
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
93
|
-
utilities/zoneinfo.py,sha256=
|
94
|
-
dycw_utilities-0.131.
|
95
|
-
dycw_utilities-0.131.
|
96
|
-
dycw_utilities-0.131.
|
97
|
-
dycw_utilities-0.131.
|
94
|
+
utilities/zoneinfo.py,sha256=gJPr9l7V8s3Y7TXpCGYEM1S81Rplb9e4MoV9Nvy2VU8,1852
|
95
|
+
dycw_utilities-0.131.14.dist-info/METADATA,sha256=jRiyVj2uOFSRzUZjiKEZHtOXb9Z-uGw3f62yd-FWWb0,1585
|
96
|
+
dycw_utilities-0.131.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
97
|
+
dycw_utilities-0.131.14.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
98
|
+
dycw_utilities-0.131.14.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/atools.py
CHANGED
@@ -5,25 +5,22 @@ from typing import TYPE_CHECKING, ParamSpec, TypeVar
|
|
5
5
|
|
6
6
|
from atools import memoize
|
7
7
|
|
8
|
-
from utilities.datetime import datetime_duration_to_timedelta
|
9
8
|
from utilities.types import Coroutine1
|
10
9
|
|
11
10
|
if TYPE_CHECKING:
|
12
|
-
|
13
|
-
|
14
|
-
from utilities.types import Duration
|
11
|
+
from whenever import TimeDelta
|
15
12
|
|
16
13
|
|
17
14
|
_P = ParamSpec("_P")
|
18
15
|
_R = TypeVar("_R")
|
19
16
|
_AsyncFunc = Callable[_P, Coroutine1[_R]]
|
20
|
-
type _Key = tuple[_AsyncFunc,
|
17
|
+
type _Key = tuple[_AsyncFunc, TimeDelta]
|
21
18
|
_MEMOIZED_FUNCS: dict[_Key, _AsyncFunc] = {}
|
22
19
|
|
23
20
|
|
24
21
|
async def call_memoized(
|
25
22
|
func: _AsyncFunc[_P, _R],
|
26
|
-
refresh:
|
23
|
+
refresh: TimeDelta | None = None,
|
27
24
|
/,
|
28
25
|
*args: _P.args,
|
29
26
|
**kwargs: _P.kwargs,
|
@@ -31,13 +28,14 @@ async def call_memoized(
|
|
31
28
|
"""Call an asynchronous function, with possible memoization."""
|
32
29
|
if refresh is None:
|
33
30
|
return await func(*args, **kwargs)
|
34
|
-
|
35
|
-
key: _Key = (func, timedelta)
|
31
|
+
key: _Key = (func, refresh)
|
36
32
|
memoized_func: _AsyncFunc[_P, _R]
|
37
33
|
try:
|
38
34
|
memoized_func = _MEMOIZED_FUNCS[key]
|
39
35
|
except KeyError:
|
40
|
-
memoized_func = _MEMOIZED_FUNCS[(key)] = memoize(duration=refresh)(
|
36
|
+
memoized_func = _MEMOIZED_FUNCS[(key)] = memoize(duration=refresh.in_seconds())(
|
37
|
+
func
|
38
|
+
)
|
41
39
|
return await memoized_func(*args, **kwargs)
|
42
40
|
|
43
41
|
|
utilities/cachetools.py
CHANGED
@@ -8,10 +8,10 @@ from typing import TYPE_CHECKING, Any, TypeVar, override
|
|
8
8
|
import cachetools
|
9
9
|
from cachetools.func import ttl_cache
|
10
10
|
|
11
|
-
from utilities.datetime import datetime_duration_to_float
|
12
|
-
|
13
11
|
if TYPE_CHECKING:
|
14
|
-
from
|
12
|
+
from whenever import TimeDelta
|
13
|
+
|
14
|
+
from utilities.types import TCallable
|
15
15
|
|
16
16
|
_K = TypeVar("_K")
|
17
17
|
_T = TypeVar("_T")
|
@@ -25,15 +25,13 @@ class TTLCache(cachetools.TTLCache[_K, _V]):
|
|
25
25
|
self,
|
26
26
|
*,
|
27
27
|
max_size: int | None = None,
|
28
|
-
max_duration:
|
28
|
+
max_duration: TimeDelta | None = None,
|
29
29
|
timer: Callable[[], float] = monotonic,
|
30
30
|
get_size_of: Callable[[Any], int] | None = None,
|
31
31
|
) -> None:
|
32
32
|
super().__init__(
|
33
33
|
maxsize=inf if max_size is None else max_size,
|
34
|
-
ttl=inf
|
35
|
-
if max_duration is None
|
36
|
-
else datetime_duration_to_float(max_duration),
|
34
|
+
ttl=inf if max_duration is None else max_duration.in_seconds(),
|
37
35
|
timer=timer,
|
38
36
|
getsizeof=get_size_of,
|
39
37
|
)
|
@@ -54,7 +52,7 @@ class TTLSet(MutableSet[_T]):
|
|
54
52
|
/,
|
55
53
|
*,
|
56
54
|
max_size: int | None = None,
|
57
|
-
max_duration:
|
55
|
+
max_duration: TimeDelta | None = None,
|
58
56
|
timer: Callable[[], float] = monotonic,
|
59
57
|
get_size_of: Callable[[Any], int] | None = None,
|
60
58
|
) -> None:
|
@@ -103,14 +101,14 @@ class TTLSet(MutableSet[_T]):
|
|
103
101
|
def cache(
|
104
102
|
*,
|
105
103
|
max_size: int | None = None,
|
106
|
-
max_duration:
|
104
|
+
max_duration: TimeDelta | None = None,
|
107
105
|
timer: Callable[[], float] = monotonic,
|
108
106
|
typed_: bool = False,
|
109
107
|
) -> Callable[[TCallable], TCallable]:
|
110
108
|
"""Decorate a function with `max_size` and/or `ttl` settings."""
|
111
109
|
return ttl_cache(
|
112
110
|
maxsize=inf if max_size is None else max_size,
|
113
|
-
ttl=inf if max_duration is None else
|
111
|
+
ttl=inf if max_duration is None else max_duration.in_seconds(),
|
114
112
|
timer=timer,
|
115
113
|
typed=typed_,
|
116
114
|
)
|
utilities/hypothesis.py
CHANGED
@@ -716,218 +716,6 @@ def lists_fixed_length(
|
|
716
716
|
##
|
717
717
|
|
718
718
|
|
719
|
-
@composite
|
720
|
-
def min_and_max_datetimes(
|
721
|
-
draw: DrawFn,
|
722
|
-
/,
|
723
|
-
*,
|
724
|
-
min_value: MaybeSearchStrategy[dt.datetime | None] = None,
|
725
|
-
max_value: MaybeSearchStrategy[dt.datetime | None] = None,
|
726
|
-
time_zone: MaybeSearchStrategy[ZoneInfo | timezone] = UTC,
|
727
|
-
round_: MathRoundMode | None = None,
|
728
|
-
timedelta: dt.timedelta | None = None,
|
729
|
-
rel_tol: float | None = None,
|
730
|
-
abs_tol: float | None = None,
|
731
|
-
valid: bool = False,
|
732
|
-
) -> tuple[dt.datetime, dt.datetime]:
|
733
|
-
"""Strategy for generating min/max datetimes."""
|
734
|
-
match min_value, max_value:
|
735
|
-
case None, None:
|
736
|
-
return draw(
|
737
|
-
pairs(
|
738
|
-
zoned_datetimes(
|
739
|
-
time_zone=time_zone,
|
740
|
-
round_=round_,
|
741
|
-
timedelta=timedelta,
|
742
|
-
rel_tol=rel_tol,
|
743
|
-
abs_tol=abs_tol,
|
744
|
-
valid=valid,
|
745
|
-
),
|
746
|
-
sorted=True,
|
747
|
-
)
|
748
|
-
)
|
749
|
-
case None, dt.datetime():
|
750
|
-
min_value_ = draw(
|
751
|
-
zoned_datetimes(
|
752
|
-
max_value=max_value,
|
753
|
-
time_zone=time_zone,
|
754
|
-
round_=round_,
|
755
|
-
timedelta=timedelta,
|
756
|
-
rel_tol=rel_tol,
|
757
|
-
abs_tol=abs_tol,
|
758
|
-
valid=valid,
|
759
|
-
)
|
760
|
-
)
|
761
|
-
return min_value_, max_value
|
762
|
-
case dt.datetime(), None:
|
763
|
-
max_value_ = draw(
|
764
|
-
zoned_datetimes(
|
765
|
-
min_value=min_value,
|
766
|
-
time_zone=time_zone,
|
767
|
-
round_=round_,
|
768
|
-
timedelta=timedelta,
|
769
|
-
rel_tol=rel_tol,
|
770
|
-
abs_tol=abs_tol,
|
771
|
-
valid=valid,
|
772
|
-
)
|
773
|
-
)
|
774
|
-
return min_value, max_value_
|
775
|
-
case dt.datetime(), dt.datetime():
|
776
|
-
_ = assume(min_value <= max_value)
|
777
|
-
return min_value, max_value
|
778
|
-
case _, _:
|
779
|
-
strategy = zoned_datetimes(
|
780
|
-
time_zone=time_zone,
|
781
|
-
round_=round_,
|
782
|
-
timedelta=timedelta,
|
783
|
-
rel_tol=rel_tol,
|
784
|
-
abs_tol=abs_tol,
|
785
|
-
valid=valid,
|
786
|
-
)
|
787
|
-
min_value_ = draw2(draw, min_value, strategy)
|
788
|
-
max_value_ = draw2(draw, max_value, strategy)
|
789
|
-
_ = assume(min_value_ <= max_value_)
|
790
|
-
return min_value_, max_value_
|
791
|
-
case _ as never:
|
792
|
-
assert_never(never)
|
793
|
-
|
794
|
-
|
795
|
-
##
|
796
|
-
|
797
|
-
|
798
|
-
@composite
|
799
|
-
def min_and_maybe_max_datetimes(
|
800
|
-
draw: DrawFn,
|
801
|
-
/,
|
802
|
-
*,
|
803
|
-
min_value: MaybeSearchStrategy[dt.datetime | None] = None,
|
804
|
-
max_value: MaybeSearchStrategy[dt.datetime | None | Sentinel] = sentinel,
|
805
|
-
time_zone: MaybeSearchStrategy[ZoneInfo | timezone] = UTC,
|
806
|
-
round_: MathRoundMode | None = None,
|
807
|
-
timedelta: dt.timedelta | None = None,
|
808
|
-
rel_tol: float | None = None,
|
809
|
-
abs_tol: float | None = None,
|
810
|
-
valid: bool = False,
|
811
|
-
) -> tuple[dt.datetime, dt.datetime | None]:
|
812
|
-
match min_value, max_value:
|
813
|
-
case None, Sentinel():
|
814
|
-
min_value_, max_value_ = draw(
|
815
|
-
pairs(
|
816
|
-
zoned_datetimes(
|
817
|
-
time_zone=time_zone,
|
818
|
-
round_=round_,
|
819
|
-
timedelta=timedelta,
|
820
|
-
rel_tol=rel_tol,
|
821
|
-
abs_tol=abs_tol,
|
822
|
-
valid=valid,
|
823
|
-
),
|
824
|
-
sorted=True,
|
825
|
-
)
|
826
|
-
)
|
827
|
-
return min_value_, draw(just(max_value_) | none())
|
828
|
-
case None, None:
|
829
|
-
min_value_ = draw(
|
830
|
-
zoned_datetimes(
|
831
|
-
time_zone=time_zone,
|
832
|
-
round_=round_,
|
833
|
-
timedelta=timedelta,
|
834
|
-
rel_tol=rel_tol,
|
835
|
-
abs_tol=abs_tol,
|
836
|
-
valid=valid,
|
837
|
-
)
|
838
|
-
)
|
839
|
-
return min_value_, None
|
840
|
-
case None, dt.datetime():
|
841
|
-
min_value_ = draw(
|
842
|
-
zoned_datetimes(
|
843
|
-
max_value=max_value,
|
844
|
-
time_zone=time_zone,
|
845
|
-
round_=round_,
|
846
|
-
timedelta=timedelta,
|
847
|
-
rel_tol=rel_tol,
|
848
|
-
abs_tol=abs_tol,
|
849
|
-
valid=valid,
|
850
|
-
)
|
851
|
-
)
|
852
|
-
return min_value_, max_value
|
853
|
-
case dt.datetime(), Sentinel():
|
854
|
-
max_value_ = draw(
|
855
|
-
zoned_datetimes(
|
856
|
-
min_value=min_value,
|
857
|
-
time_zone=time_zone,
|
858
|
-
round_=round_,
|
859
|
-
timedelta=timedelta,
|
860
|
-
rel_tol=rel_tol,
|
861
|
-
abs_tol=abs_tol,
|
862
|
-
valid=valid,
|
863
|
-
)
|
864
|
-
| none()
|
865
|
-
)
|
866
|
-
return min_value, max_value_
|
867
|
-
case dt.datetime(), None:
|
868
|
-
return min_value, None
|
869
|
-
case dt.datetime(), dt.datetime():
|
870
|
-
_ = assume(min_value <= max_value)
|
871
|
-
return min_value, max_value
|
872
|
-
case _, _:
|
873
|
-
strategy = zoned_datetimes(
|
874
|
-
time_zone=time_zone,
|
875
|
-
round_=round_,
|
876
|
-
timedelta=timedelta,
|
877
|
-
rel_tol=rel_tol,
|
878
|
-
abs_tol=abs_tol,
|
879
|
-
valid=valid,
|
880
|
-
)
|
881
|
-
min_value_ = draw2(draw, min_value, strategy)
|
882
|
-
max_value_ = draw2(draw, max_value, strategy | none(), sentinel=True)
|
883
|
-
_ = assume((max_value_ is None) or (min_value_ <= max_value_))
|
884
|
-
return min_value_, max_value_
|
885
|
-
case _ as never:
|
886
|
-
assert_never(never)
|
887
|
-
|
888
|
-
|
889
|
-
##
|
890
|
-
|
891
|
-
|
892
|
-
@composite
|
893
|
-
def min_and_maybe_max_sizes(
|
894
|
-
draw: DrawFn,
|
895
|
-
/,
|
896
|
-
*,
|
897
|
-
min_value: MaybeSearchStrategy[int | None] = None,
|
898
|
-
max_value: MaybeSearchStrategy[int | None | Sentinel] = sentinel,
|
899
|
-
) -> tuple[int, int | None]:
|
900
|
-
match min_value, max_value:
|
901
|
-
case None, Sentinel():
|
902
|
-
min_value_, max_value_ = draw(pairs(integers(min_value=0), sorted=True))
|
903
|
-
return min_value_, draw(just(max_value_) | none())
|
904
|
-
case None, None:
|
905
|
-
min_value_ = draw(integers(min_value=0))
|
906
|
-
return min_value_, None
|
907
|
-
case None, int():
|
908
|
-
min_value_ = draw(integers(0, max_value))
|
909
|
-
return min_value_, max_value
|
910
|
-
case int(), Sentinel():
|
911
|
-
max_value_ = draw(integers(min_value=min_value) | none())
|
912
|
-
return min_value, max_value_
|
913
|
-
case int(), None:
|
914
|
-
return min_value, None
|
915
|
-
case int(), int():
|
916
|
-
_ = assume(min_value <= max_value)
|
917
|
-
return min_value, max_value
|
918
|
-
case _, _:
|
919
|
-
strategy = integers(min_value=0)
|
920
|
-
min_value_ = draw2(draw, min_value, strategy)
|
921
|
-
max_value_ = draw2(draw, max_value, strategy | none(), sentinel=True)
|
922
|
-
_ = assume((max_value_ is None) or (min_value_ <= max_value_))
|
923
|
-
return min_value_, max_value_
|
924
|
-
case _ as never:
|
925
|
-
assert_never(never)
|
926
|
-
|
927
|
-
|
928
|
-
##
|
929
|
-
|
930
|
-
|
931
719
|
@composite
|
932
720
|
def months(
|
933
721
|
draw: DrawFn,
|
@@ -1694,17 +1482,12 @@ __all__ = [
|
|
1694
1482
|
"int64s",
|
1695
1483
|
"int_arrays",
|
1696
1484
|
"lists_fixed_length",
|
1697
|
-
"min_and_max_datetimes",
|
1698
|
-
"min_and_maybe_max_datetimes",
|
1699
|
-
"min_and_maybe_max_sizes",
|
1700
|
-
"min_and_maybe_max_sizes",
|
1701
1485
|
"months",
|
1702
1486
|
"namespace_mixins",
|
1703
1487
|
"numbers",
|
1704
1488
|
"pairs",
|
1705
1489
|
"paths",
|
1706
1490
|
"plain_datetimes",
|
1707
|
-
"plain_datetimes",
|
1708
1491
|
"plain_datetimes_whenever",
|
1709
1492
|
"random_states",
|
1710
1493
|
"sentinels",
|
utilities/orjson.py
CHANGED
@@ -760,7 +760,7 @@ class OrjsonFormatter(Formatter):
|
|
760
760
|
path_name=Path(record.pathname),
|
761
761
|
line_num=record.lineno,
|
762
762
|
message=record.getMessage(),
|
763
|
-
datetime=from_timestamp(record.created, time_zone=
|
763
|
+
datetime=from_timestamp(record.created, time_zone=LOCAL_TIME_ZONE),
|
764
764
|
func_name=record.funcName,
|
765
765
|
extra=extra if len(extra) >= 1 else None,
|
766
766
|
)
|
utilities/period.py
CHANGED
@@ -1,42 +1,20 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
|
4
|
-
from
|
5
|
-
from
|
6
|
-
|
7
|
-
from
|
8
|
-
|
9
|
-
|
10
|
-
Literal,
|
11
|
-
Self,
|
12
|
-
TypedDict,
|
13
|
-
TypeVar,
|
14
|
-
assert_never,
|
15
|
-
cast,
|
16
|
-
override,
|
17
|
-
)
|
18
|
-
|
19
|
-
from utilities.datetime import ZERO_TIME
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import TYPE_CHECKING, Generic, Self, TypedDict, TypeVar, override
|
5
|
+
from zoneinfo import ZoneInfo
|
6
|
+
|
7
|
+
from whenever import Date, DateDelta, TimeDelta, ZonedDateTime
|
8
|
+
|
9
|
+
from utilities.dataclasses import replace_non_sentinel
|
20
10
|
from utilities.functions import get_class_name
|
21
|
-
from utilities.iterables import OneUniqueNonUniqueError, always_iterable, one_unique
|
22
11
|
from utilities.sentinel import Sentinel, sentinel
|
23
|
-
from utilities.
|
24
|
-
from utilities.whenever import (
|
25
|
-
serialize_date,
|
26
|
-
serialize_plain_datetime,
|
27
|
-
serialize_zoned_datetime,
|
28
|
-
)
|
29
|
-
from utilities.zoneinfo import EnsureTimeZoneError, ensure_time_zone
|
12
|
+
from utilities.zoneinfo import get_time_zone_name
|
30
13
|
|
31
14
|
if TYPE_CHECKING:
|
32
|
-
from
|
33
|
-
|
34
|
-
from utilities.iterables import MaybeIterable
|
35
|
-
from utilities.types import DateOrDateTime
|
15
|
+
from utilities.types import TimeZoneLike
|
36
16
|
|
37
|
-
|
38
|
-
type _DateOrDateTime = Literal["date", "datetime"]
|
39
|
-
_TPeriod = TypeVar("_TPeriod", dt.date, dt.datetime)
|
17
|
+
_TPeriod = TypeVar("_TPeriod", Date, ZonedDateTime)
|
40
18
|
|
41
19
|
|
42
20
|
class _PeriodAsDict(TypedDict, Generic[_TPeriod]):
|
@@ -44,281 +22,133 @@ class _PeriodAsDict(TypedDict, Generic[_TPeriod]):
|
|
44
22
|
end: _TPeriod
|
45
23
|
|
46
24
|
|
47
|
-
@dataclass(repr=False, order=True, unsafe_hash=True)
|
48
|
-
class
|
49
|
-
"""A period of
|
25
|
+
@dataclass(repr=False, order=True, unsafe_hash=True, kw_only=False)
|
26
|
+
class DatePeriod:
|
27
|
+
"""A period of dates."""
|
50
28
|
|
51
|
-
start:
|
52
|
-
end:
|
53
|
-
req_duration: MaybeIterable[dt.timedelta] | None = field(
|
54
|
-
default=None, repr=False, kw_only=True
|
55
|
-
)
|
56
|
-
min_duration: dt.timedelta | None = field(default=None, repr=False, kw_only=True)
|
57
|
-
max_duration: dt.timedelta | None = field(default=None, repr=False, kw_only=True)
|
29
|
+
start: Date
|
30
|
+
end: Date
|
58
31
|
|
59
32
|
def __post_init__(self) -> None:
|
60
|
-
if
|
61
|
-
is_instance_gen(left, cls) is not is_instance_gen(right, cls)
|
62
|
-
for left, right in permutations([self.start, self.end], 2)
|
63
|
-
for cls in [dt.date, dt.datetime]
|
64
|
-
):
|
65
|
-
raise _PeriodDateAndDateTimeMixedError(start=self.start, end=self.end)
|
66
|
-
for date in [self.start, self.end]:
|
67
|
-
if isinstance(date, dt.datetime):
|
68
|
-
try:
|
69
|
-
_ = ensure_time_zone(date)
|
70
|
-
except EnsureTimeZoneError:
|
71
|
-
raise _PeriodNaiveDateTimeError(
|
72
|
-
start=self.start, end=self.end
|
73
|
-
) from None
|
74
|
-
duration = self.end - self.start
|
75
|
-
if duration < ZERO_TIME:
|
33
|
+
if self.start > self.end:
|
76
34
|
raise _PeriodInvalidError(start=self.start, end=self.end)
|
77
|
-
if (self.req_duration is not None) and (
|
78
|
-
duration not in always_iterable(self.req_duration)
|
79
|
-
):
|
80
|
-
raise _PeriodReqDurationError(
|
81
|
-
start=self.start,
|
82
|
-
end=self.end,
|
83
|
-
duration=duration,
|
84
|
-
req_duration=self.req_duration,
|
85
|
-
)
|
86
|
-
if (self.min_duration is not None) and (duration < self.min_duration):
|
87
|
-
raise _PeriodMinDurationError(
|
88
|
-
start=self.start,
|
89
|
-
end=self.end,
|
90
|
-
duration=duration,
|
91
|
-
min_duration=self.min_duration,
|
92
|
-
)
|
93
|
-
if (self.max_duration is not None) and (duration > self.max_duration):
|
94
|
-
raise _PeriodMaxDurationError(
|
95
|
-
start=self.start,
|
96
|
-
end=self.end,
|
97
|
-
duration=duration,
|
98
|
-
max_duration=self.max_duration,
|
99
|
-
)
|
100
35
|
|
101
|
-
def __add__(self, other:
|
36
|
+
def __add__(self, other: DateDelta, /) -> Self:
|
102
37
|
"""Offset the period."""
|
103
38
|
return self.replace(start=self.start + other, end=self.end + other)
|
104
39
|
|
105
|
-
def __contains__(self, other:
|
40
|
+
def __contains__(self, other: Date, /) -> bool:
|
106
41
|
"""Check if a date/datetime lies in the period."""
|
107
|
-
match self.kind:
|
108
|
-
case "date":
|
109
|
-
if isinstance(other, dt.datetime):
|
110
|
-
raise _PeriodDateContainsDateTimeError(
|
111
|
-
start=self.start, end=self.end
|
112
|
-
)
|
113
|
-
case "datetime":
|
114
|
-
if not isinstance(other, dt.datetime):
|
115
|
-
raise _PeriodDateTimeContainsDateError(
|
116
|
-
start=self.start, end=self.end
|
117
|
-
)
|
118
|
-
case _ as never:
|
119
|
-
assert_never(never)
|
120
42
|
return self.start <= other <= self.end
|
121
43
|
|
122
44
|
@override
|
123
45
|
def __repr__(self) -> str:
|
124
46
|
cls = get_class_name(self)
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
start, end = map(serialize_date, [result.start, result.end])
|
129
|
-
return f"{cls}({start}, {end})"
|
130
|
-
case "datetime":
|
131
|
-
result = cast("Period[dt.datetime]", self)
|
132
|
-
try:
|
133
|
-
time_zone = result.time_zone
|
134
|
-
except _PeriodTimeZoneNonUniqueError:
|
135
|
-
start, end = map(
|
136
|
-
serialize_zoned_datetime, [result.start, result.end]
|
137
|
-
)
|
138
|
-
return f"{cls}({start}, {end})"
|
139
|
-
start, end = (
|
140
|
-
serialize_plain_datetime(t.replace(tzinfo=None))
|
141
|
-
for t in [result.start, result.end]
|
142
|
-
)
|
143
|
-
return f"{cls}({start}, {end}, {time_zone})"
|
144
|
-
case _ as never:
|
145
|
-
assert_never(never)
|
146
|
-
|
147
|
-
def __sub__(self, other: dt.timedelta, /) -> Self:
|
47
|
+
return f"{cls}({self.start}, {self.end})"
|
48
|
+
|
49
|
+
def __sub__(self, other: DateDelta, /) -> Self:
|
148
50
|
"""Offset the period."""
|
149
51
|
return self.replace(start=self.start - other, end=self.end - other)
|
150
52
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
case "date":
|
155
|
-
raise _PeriodAsTimeZoneInapplicableError(start=self.start, end=self.end)
|
156
|
-
case "datetime":
|
157
|
-
result = cast("Period[dt.datetime]", self)
|
158
|
-
result = result.replace(
|
159
|
-
start=result.start.astimezone(time_zone),
|
160
|
-
end=result.end.astimezone(time_zone),
|
161
|
-
)
|
162
|
-
return cast("Self", result)
|
163
|
-
case _ as never:
|
164
|
-
assert_never(never)
|
165
|
-
|
166
|
-
@cached_property
|
167
|
-
def duration(self) -> dt.timedelta:
|
168
|
-
"""The duration of the period."""
|
53
|
+
@property
|
54
|
+
def delta(self) -> DateDelta:
|
55
|
+
"""The delta of the period."""
|
169
56
|
return self.end - self.start
|
170
57
|
|
171
|
-
@cached_property
|
172
|
-
def kind(self) -> _DateOrDateTime:
|
173
|
-
"""The kind of the period."""
|
174
|
-
return "date" if is_instance_gen(self.start, dt.date) else "datetime"
|
175
|
-
|
176
58
|
def replace(
|
177
|
-
self,
|
178
|
-
*,
|
179
|
-
start: _TPeriod | None = None,
|
180
|
-
end: _TPeriod | None = None,
|
181
|
-
req_duration: MaybeIterable[dt.timedelta] | None | Sentinel = sentinel,
|
182
|
-
min_duration: dt.timedelta | None | Sentinel = sentinel,
|
183
|
-
max_duration: dt.timedelta | None | Sentinel = sentinel,
|
59
|
+
self, *, start: Date | Sentinel = sentinel, end: Date | Sentinel = sentinel
|
184
60
|
) -> Self:
|
185
61
|
"""Replace elements of the period."""
|
186
|
-
return
|
187
|
-
self.start if start is None else start,
|
188
|
-
self.end if end is None else end,
|
189
|
-
req_duration=self.req_duration
|
190
|
-
if isinstance(req_duration, Sentinel)
|
191
|
-
else req_duration,
|
192
|
-
min_duration=self.min_duration
|
193
|
-
if isinstance(min_duration, Sentinel)
|
194
|
-
else min_duration,
|
195
|
-
max_duration=self.max_duration
|
196
|
-
if isinstance(max_duration, Sentinel)
|
197
|
-
else max_duration,
|
198
|
-
)
|
199
|
-
|
200
|
-
@cached_property
|
201
|
-
def time_zone(self) -> ZoneInfo:
|
202
|
-
"""The time zone of the period."""
|
203
|
-
match self.kind:
|
204
|
-
case "date":
|
205
|
-
raise _PeriodTimeZoneInapplicableError(
|
206
|
-
start=self.start, end=self.end
|
207
|
-
) from None
|
208
|
-
case "datetime":
|
209
|
-
result = cast("Period[dt.datetime]", self)
|
210
|
-
try:
|
211
|
-
return one_unique(map(ensure_time_zone, [result.start, result.end]))
|
212
|
-
except OneUniqueNonUniqueError as error:
|
213
|
-
raise _PeriodTimeZoneNonUniqueError(
|
214
|
-
start=self.start,
|
215
|
-
end=self.end,
|
216
|
-
first=error.first,
|
217
|
-
second=error.second,
|
218
|
-
) from None
|
219
|
-
case _ as never:
|
220
|
-
assert_never(never)
|
221
|
-
|
222
|
-
def to_dict(self) -> _PeriodAsDict:
|
223
|
-
"""Convert the period to a dictionary."""
|
224
|
-
return {"start": self.start, "end": self.end}
|
225
|
-
|
226
|
-
|
227
|
-
@dataclass(kw_only=True, slots=True)
|
228
|
-
class PeriodError(Generic[_TPeriod], Exception):
|
229
|
-
start: _TPeriod
|
230
|
-
end: _TPeriod
|
62
|
+
return replace_non_sentinel(self, start=start, end=end)
|
231
63
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
@override
|
236
|
-
def __str__(self) -> str:
|
237
|
-
return f"Invalid period; got date and datetime mix ({self.start}, {self.end})"
|
64
|
+
def to_dict(self) -> _PeriodAsDict[Date]:
|
65
|
+
"""Convert the period to a dictionary."""
|
66
|
+
return _PeriodAsDict(start=self.start, end=self.end)
|
238
67
|
|
239
68
|
|
240
|
-
@dataclass(
|
241
|
-
class
|
242
|
-
|
243
|
-
def __str__(self) -> str:
|
244
|
-
return f"Invalid period; got naive datetime(s) ({self.start}, {self.end})"
|
69
|
+
@dataclass(repr=False, order=True, unsafe_hash=True, kw_only=False)
|
70
|
+
class ZonedDateTimePeriod:
|
71
|
+
"""A period of time."""
|
245
72
|
|
73
|
+
start: ZonedDateTime
|
74
|
+
end: ZonedDateTime
|
246
75
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
76
|
+
def __post_init__(self) -> None:
|
77
|
+
if self.start > self.end:
|
78
|
+
raise _PeriodInvalidError(start=self.start, end=self.end)
|
79
|
+
if self.start.tz != self.end.tz:
|
80
|
+
raise _PeriodTimeZoneError(
|
81
|
+
start=ZoneInfo(self.start.tz), end=ZoneInfo(self.end.tz)
|
82
|
+
)
|
252
83
|
|
84
|
+
def __add__(self, other: TimeDelta, /) -> Self:
|
85
|
+
"""Offset the period."""
|
86
|
+
return self.replace(start=self.start + other, end=self.end + other)
|
253
87
|
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
req_duration: MaybeIterable[dt.timedelta]
|
88
|
+
def __contains__(self, other: ZonedDateTime, /) -> bool:
|
89
|
+
"""Check if a date/datetime lies in the period."""
|
90
|
+
return self.start <= other <= self.end
|
258
91
|
|
259
92
|
@override
|
260
|
-
def
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
@dataclass(kw_only=True, slots=True)
|
265
|
-
class _PeriodMinDurationError(PeriodError[_TPeriod]):
|
266
|
-
duration: dt.timedelta
|
267
|
-
min_duration: dt.timedelta
|
93
|
+
def __repr__(self) -> str:
|
94
|
+
cls = get_class_name(self)
|
95
|
+
return f"{cls}({self.start.to_plain()}, {self.end.to_plain()}[{self.time_zone.key}])"
|
268
96
|
|
269
|
-
|
270
|
-
|
271
|
-
return (
|
272
|
-
f"Period must have min duration {self.min_duration}; got {self.duration})"
|
273
|
-
)
|
97
|
+
def __sub__(self, other: TimeDelta, /) -> Self:
|
98
|
+
"""Offset the period."""
|
99
|
+
return self.replace(start=self.start - other, end=self.end - other)
|
274
100
|
|
101
|
+
@property
|
102
|
+
def delta(self) -> TimeDelta:
|
103
|
+
"""The duration of the period."""
|
104
|
+
return self.end - self.start
|
275
105
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
106
|
+
def replace(
|
107
|
+
self,
|
108
|
+
*,
|
109
|
+
start: ZonedDateTime | Sentinel = sentinel,
|
110
|
+
end: ZonedDateTime | Sentinel = sentinel,
|
111
|
+
) -> Self:
|
112
|
+
"""Replace elements of the period."""
|
113
|
+
return replace_non_sentinel(self, start=start, end=end)
|
280
114
|
|
281
|
-
@
|
282
|
-
def
|
283
|
-
|
115
|
+
@property
|
116
|
+
def time_zone(self) -> ZoneInfo:
|
117
|
+
"""The time zone of the period."""
|
118
|
+
return ZoneInfo(self.start.tz)
|
284
119
|
|
120
|
+
def to_dict(self) -> _PeriodAsDict[ZonedDateTime]:
|
121
|
+
"""Convert the period to a dictionary."""
|
122
|
+
return _PeriodAsDict(start=self.start, end=self.end)
|
285
123
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
return "Period of dates does not have a timezone attribute"
|
124
|
+
def to_tz(self, time_zone: TimeZoneLike, /) -> Self:
|
125
|
+
"""Convert the time zone."""
|
126
|
+
tz = get_time_zone_name(time_zone)
|
127
|
+
return self.replace(start=self.start.to_tz(tz), end=self.end.to_tz(tz))
|
291
128
|
|
292
129
|
|
293
130
|
@dataclass(kw_only=True, slots=True)
|
294
|
-
class
|
295
|
-
@override
|
296
|
-
def __str__(self) -> str:
|
297
|
-
return "Period of dates cannot contain datetimes"
|
131
|
+
class PeriodError(Exception): ...
|
298
132
|
|
299
133
|
|
300
134
|
@dataclass(kw_only=True, slots=True)
|
301
|
-
class
|
302
|
-
|
303
|
-
|
304
|
-
return "Period of datetimes cannot contain dates"
|
305
|
-
|
135
|
+
class _PeriodInvalidError(PeriodError, Generic[_TPeriod]):
|
136
|
+
start: _TPeriod
|
137
|
+
end: _TPeriod
|
306
138
|
|
307
|
-
@dataclass(kw_only=True, slots=True)
|
308
|
-
class _PeriodTimeZoneInapplicableError(PeriodError[_TPeriod]):
|
309
139
|
@override
|
310
140
|
def __str__(self) -> str:
|
311
|
-
return "
|
141
|
+
return f"Invalid period; got {self.start} > {self.end}"
|
312
142
|
|
313
143
|
|
314
144
|
@dataclass(kw_only=True, slots=True)
|
315
|
-
class
|
316
|
-
|
317
|
-
|
145
|
+
class _PeriodTimeZoneError(PeriodError):
|
146
|
+
start: ZoneInfo
|
147
|
+
end: ZoneInfo
|
318
148
|
|
319
149
|
@override
|
320
150
|
def __str__(self) -> str:
|
321
|
-
return f"Period must contain exactly one time zone; got {self.
|
151
|
+
return f"Period must contain exactly one time zone; got {self.start} and {self.end}"
|
322
152
|
|
323
153
|
|
324
|
-
__all__ = ["
|
154
|
+
__all__ = ["DatePeriod", "PeriodError", "ZonedDateTimePeriod"]
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING, Any, TypeVar, override
|
4
|
+
|
5
|
+
from typed_settings.converters import TSConverter
|
6
|
+
from whenever import (
|
7
|
+
Date,
|
8
|
+
DateDelta,
|
9
|
+
DateTimeDelta,
|
10
|
+
PlainDateTime,
|
11
|
+
Time,
|
12
|
+
TimeDelta,
|
13
|
+
ZonedDateTime,
|
14
|
+
)
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from collections.abc import Callable
|
18
|
+
|
19
|
+
|
20
|
+
_T = TypeVar("_T")
|
21
|
+
|
22
|
+
|
23
|
+
class ExtendedTSConverter(TSConverter):
|
24
|
+
"""An extension of the TSConverter for custom types."""
|
25
|
+
|
26
|
+
@override
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
*,
|
30
|
+
resolve_paths: bool = True,
|
31
|
+
strlist_sep: str | Callable[[str], list] | None = ":",
|
32
|
+
) -> None:
|
33
|
+
super().__init__(resolve_paths=resolve_paths, strlist_sep=strlist_sep)
|
34
|
+
cases: list[tuple[type[Any], Callable[..., Any]]] = [
|
35
|
+
(Date, Date.parse_common_iso),
|
36
|
+
(DateDelta, DateDelta.parse_common_iso),
|
37
|
+
(DateTimeDelta, DateTimeDelta.parse_common_iso),
|
38
|
+
(PlainDateTime, PlainDateTime.parse_common_iso),
|
39
|
+
(Time, Time.parse_common_iso),
|
40
|
+
(TimeDelta, TimeDelta.parse_common_iso),
|
41
|
+
(ZonedDateTime, ZonedDateTime.parse_common_iso),
|
42
|
+
]
|
43
|
+
extras = {cls: _make_converter(cls, func) for cls, func in cases}
|
44
|
+
self.scalar_converters |= extras
|
45
|
+
|
46
|
+
|
47
|
+
def _make_converter(
|
48
|
+
cls: type[_T], parser: Callable[[str], _T], /
|
49
|
+
) -> Callable[[Any, type[Any]], Any]:
|
50
|
+
def hook(value: _T | str, _: type[_T] = cls, /) -> Any:
|
51
|
+
if not isinstance(value, (cls, str)): # pragma: no cover
|
52
|
+
msg = f"Invalid type {type(value).__name__!r}; expected '{cls.__name__}' or 'str'"
|
53
|
+
raise TypeError(msg)
|
54
|
+
if isinstance(value, str):
|
55
|
+
return parser(value)
|
56
|
+
return value
|
57
|
+
|
58
|
+
return hook
|
59
|
+
|
60
|
+
|
61
|
+
__all__ = ["ExtendedTSConverter"]
|
utilities/tzdata.py
CHANGED
@@ -1,63 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import TYPE_CHECKING
|
4
3
|
from zoneinfo import ZoneInfo
|
5
4
|
|
6
|
-
from utilities.datetime import get_now, get_today
|
7
|
-
|
8
|
-
if TYPE_CHECKING:
|
9
|
-
import datetime as dt
|
10
|
-
|
11
|
-
|
12
5
|
HongKong = ZoneInfo("Asia/Hong_Kong")
|
13
6
|
Tokyo = ZoneInfo("Asia/Tokyo")
|
14
7
|
USCentral = ZoneInfo("US/Central")
|
15
8
|
USEastern = ZoneInfo("US/Eastern")
|
16
9
|
|
17
10
|
|
18
|
-
|
19
|
-
"""Get the current time in Hong Kong."""
|
20
|
-
return get_now(time_zone=HongKong)
|
21
|
-
|
22
|
-
|
23
|
-
NOW_HONG_KONG = get_now_hong_kong()
|
24
|
-
|
25
|
-
|
26
|
-
def get_now_tokyo() -> dt.datetime:
|
27
|
-
"""Get the current time in Tokyo."""
|
28
|
-
return get_now(time_zone=Tokyo)
|
29
|
-
|
30
|
-
|
31
|
-
NOW_TOKYO = get_now_tokyo()
|
32
|
-
|
33
|
-
|
34
|
-
def get_today_hong_kong() -> dt.date:
|
35
|
-
"""Get the current date in Hong Kong."""
|
36
|
-
return get_today(time_zone=HongKong)
|
37
|
-
|
38
|
-
|
39
|
-
TODAY_HONG_KONG = get_today_hong_kong()
|
40
|
-
|
41
|
-
|
42
|
-
def get_today_tokyo() -> dt.date:
|
43
|
-
"""Get the current date in Tokyo."""
|
44
|
-
return get_today(time_zone=Tokyo)
|
45
|
-
|
46
|
-
|
47
|
-
TODAY_TOKYO = get_today_tokyo()
|
48
|
-
|
49
|
-
|
50
|
-
__all__ = [
|
51
|
-
"NOW_HONG_KONG",
|
52
|
-
"NOW_TOKYO",
|
53
|
-
"TODAY_HONG_KONG",
|
54
|
-
"TODAY_TOKYO",
|
55
|
-
"HongKong",
|
56
|
-
"Tokyo",
|
57
|
-
"USCentral",
|
58
|
-
"USEastern",
|
59
|
-
"get_now_hong_kong",
|
60
|
-
"get_now_tokyo",
|
61
|
-
"get_today_hong_kong",
|
62
|
-
"get_today_tokyo",
|
63
|
-
]
|
11
|
+
__all__ = ["HongKong", "Tokyo", "USCentral", "USEastern"]
|
utilities/whenever2.py
CHANGED
@@ -18,7 +18,7 @@ from whenever import (
|
|
18
18
|
|
19
19
|
from utilities.datetime import maybe_sub_pct_y
|
20
20
|
from utilities.sentinel import Sentinel, sentinel
|
21
|
-
from utilities.tzlocal import LOCAL_TIME_ZONE_NAME
|
21
|
+
from utilities.tzlocal import LOCAL_TIME_ZONE, LOCAL_TIME_ZONE_NAME
|
22
22
|
from utilities.zoneinfo import UTC, get_time_zone_name
|
23
23
|
|
24
24
|
if TYPE_CHECKING:
|
@@ -111,7 +111,7 @@ NOW_UTC = get_now(time_zone=UTC)
|
|
111
111
|
|
112
112
|
def get_now_local() -> ZonedDateTime:
|
113
113
|
"""Get the current local time."""
|
114
|
-
return get_now(time_zone=
|
114
|
+
return get_now(time_zone=LOCAL_TIME_ZONE)
|
115
115
|
|
116
116
|
|
117
117
|
NOW_LOCAL = get_now_local()
|
@@ -130,7 +130,7 @@ TODAY_UTC = get_today(time_zone=UTC)
|
|
130
130
|
|
131
131
|
def get_today_local() -> Date:
|
132
132
|
"""Get the current, timezone-aware local date."""
|
133
|
-
return get_today(time_zone=
|
133
|
+
return get_today(time_zone=LOCAL_TIME_ZONE)
|
134
134
|
|
135
135
|
|
136
136
|
TODAY_LOCAL = get_today_local()
|
@@ -267,6 +267,7 @@ __all__ = [
|
|
267
267
|
"ZONED_DATE_TIME_MIN",
|
268
268
|
"WheneverLogRecord",
|
269
269
|
"format_compact",
|
270
|
+
"format_compact",
|
270
271
|
"from_timestamp",
|
271
272
|
"from_timestamp_millis",
|
272
273
|
"from_timestamp_nanos",
|
utilities/zoneinfo.py
CHANGED
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|
5
5
|
from typing import TYPE_CHECKING, assert_never, cast, override
|
6
6
|
from zoneinfo import ZoneInfo
|
7
7
|
|
8
|
-
from utilities.tzlocal import
|
8
|
+
from utilities.tzlocal import LOCAL_TIME_ZONE
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from utilities.types import TimeZone, TimeZoneLike
|
@@ -23,7 +23,7 @@ def ensure_time_zone(obj: TimeZoneLike, /) -> ZoneInfo:
|
|
23
23
|
case ZoneInfo() as zone_info:
|
24
24
|
return zone_info
|
25
25
|
case "local":
|
26
|
-
return
|
26
|
+
return LOCAL_TIME_ZONE
|
27
27
|
case str() as key:
|
28
28
|
return ZoneInfo(key)
|
29
29
|
case dt.tzinfo() as tzinfo:
|
File without changes
|
File without changes
|