dycw-utilities 0.131.10__py3-none-any.whl → 0.131.12__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.
utilities/math.py CHANGED
@@ -8,9 +8,10 @@ from re import Match, search
8
8
  from typing import TYPE_CHECKING, Literal, assert_never, overload, override
9
9
 
10
10
  from utilities.errors import ImpossibleCaseError
11
+ from utilities.re import ExtractGroupsError, extract_groups
11
12
 
12
13
  if TYPE_CHECKING:
13
- from utilities.types import Number, RoundMode, Sign
14
+ from utilities.types import MathRoundMode, Number, Sign
14
15
 
15
16
 
16
17
  MIN_FLOAT32, MAX_FLOAT32 = -3.4028234663852886e38, 3.4028234663852886e38
@@ -708,7 +709,7 @@ def round_(
708
709
  x: float,
709
710
  /,
710
711
  *,
711
- mode: RoundMode = "standard",
712
+ mode: MathRoundMode = "standard",
712
713
  rel_tol: float | None = None,
713
714
  abs_tol: float | None = None,
714
715
  ) -> int:
@@ -748,7 +749,7 @@ def round_(
748
749
 
749
750
  def _round_tie_standard(
750
751
  x: float,
751
- mode: RoundMode,
752
+ mode: MathRoundMode,
752
753
  /,
753
754
  *,
754
755
  rel_tol: float | None = None,
@@ -757,9 +758,9 @@ def _round_tie_standard(
757
758
  """Round a float to an integer using the standard method."""
758
759
  frac, _ = modf(x)
759
760
  if _is_close(abs(frac), 0.5, rel_tol=rel_tol, abs_tol=abs_tol):
760
- mode_use: RoundMode = mode
761
+ mode_use: MathRoundMode = mode
761
762
  else:
762
- mode_use: RoundMode = "standard"
763
+ mode_use: MathRoundMode = "standard"
763
764
  return round_(x, mode=mode_use)
764
765
 
765
766
 
@@ -775,9 +776,9 @@ def round_float_imprecisions(
775
776
  ) -> float:
776
777
  """Round a float, removing binary representation imprecisions."""
777
778
  try:
778
- ((head, tail),) = _ROUND_FLOAT_IMPRECISIONS_PATTERN.findall(str(x))
779
- except ValueError:
780
- ((head, tail),) = _ROUND_FLOAT_IMPRECISIONS_PATTERN.findall(f"{x:.20f}")
779
+ head, tail = extract_groups(_ROUND_FLOAT_IMPRECISIONS_PATTERN, str(x))
780
+ except ExtractGroupsError:
781
+ head, tail = extract_groups(_ROUND_FLOAT_IMPRECISIONS_PATTERN, f"{x:.20f}")
781
782
  half = ceil(decimals / 2)
782
783
  pattern0 = search(rf"^([0-9]+?)(0{{{half},}})([0-9]+?)$", tail)
783
784
  pattern9 = search(rf"^(0*)([0-9]+?)(9{{{half},}})([0-9]+?)$", tail)
@@ -823,7 +824,7 @@ def round_to_float(
823
824
  y: float,
824
825
  /,
825
826
  *,
826
- mode: RoundMode = "standard",
827
+ mode: MathRoundMode = "standard",
827
828
  rel_tol: float | None = None,
828
829
  abs_tol: float | None = None,
829
830
  ) -> float:
utilities/orjson.py CHANGED
@@ -14,6 +14,7 @@ from pathlib import Path
14
14
  from re import Pattern, search
15
15
  from typing import TYPE_CHECKING, Any, Literal, Self, assert_never, overload, override
16
16
  from uuid import UUID
17
+ from zoneinfo import ZoneInfo
17
18
 
18
19
  from orjson import (
19
20
  OPT_PASSTHROUGH_DATACLASS,
@@ -22,6 +23,7 @@ from orjson import (
22
23
  dumps,
23
24
  loads,
24
25
  )
26
+ from whenever import ZonedDateTime
25
27
 
26
28
  from utilities.concurrent import concurrent_map
27
29
  from utilities.dataclasses import dataclass_to_dict
@@ -35,15 +37,8 @@ from utilities.iterables import (
35
37
  )
36
38
  from utilities.logging import get_logging_level_number
37
39
  from utilities.math import MAX_INT64, MIN_INT64
38
- from utilities.types import (
39
- Dataclass,
40
- DateOrDateTime,
41
- LogLevel,
42
- MaybeIterable,
43
- PathLike,
44
- StrMapping,
45
- )
46
- from utilities.tzlocal import get_local_time_zone
40
+ from utilities.types import Dataclass, LogLevel, MaybeIterable, PathLike, StrMapping
41
+ from utilities.tzlocal import LOCAL_TIME_ZONE
47
42
  from utilities.uuid import UUID_PATTERN
48
43
  from utilities.version import Version, parse_version
49
44
  from utilities.whenever import (
@@ -57,12 +52,14 @@ from utilities.whenever import (
57
52
  serialize_time,
58
53
  serialize_timedelta,
59
54
  )
60
- from utilities.zoneinfo import ensure_time_zone
55
+ from utilities.whenever2 import from_timestamp
61
56
 
62
57
  if TYPE_CHECKING:
63
58
  from collections.abc import Set as AbstractSet
64
59
  from logging import _FormatStyle
65
60
 
61
+ from whenever import Date
62
+
66
63
  from utilities.types import Parallelism
67
64
 
68
65
 
@@ -92,6 +89,7 @@ class _Prefixes(Enum):
92
89
  unserializable = "un"
93
90
  uuid = "uu"
94
91
  version = "v"
92
+ zoned_datetime = "zd"
95
93
 
96
94
 
97
95
  type _DataclassHook = Callable[[type[Dataclass], StrMapping], StrMapping]
@@ -194,6 +192,8 @@ def _pre_process(
194
192
  return f"[{_Prefixes.exception_class.value}|{error_cls.__qualname__}]"
195
193
  case Version() as version:
196
194
  return f"[{_Prefixes.version.value}]{version!s}"
195
+ case ZonedDateTime() as datetime:
196
+ return f"[{_Prefixes.zoned_datetime.value}]{datetime}"
197
197
  # contains
198
198
  case Dataclass() as dataclass:
199
199
  asdict = dataclass_to_dict(
@@ -350,6 +350,11 @@ _ZONED_DATETIME_PATTERN = re.compile(
350
350
  + _Prefixes.datetime.value
351
351
  + r"\](\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?[\+\-]\d{2}:\d{2}(?::\d{2})?\[(?!(?:dt\.)).+?\])$"
352
352
  )
353
+ _ZONED_DATETIME_PATTERN2 = re.compile(
354
+ r"^\["
355
+ + _Prefixes.zoned_datetime.value
356
+ + r"\](\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?[\+\-]\d{2}:\d{2}(?::\d{2})?\[(?!(?:dt\.)).+?\])$"
357
+ )
353
358
 
354
359
 
355
360
  def _make_unit_pattern(prefix: _Prefixes, /) -> Pattern[str]:
@@ -437,6 +442,8 @@ def _object_hook(
437
442
  return parse_version(match.group(1))
438
443
  if match := _ZONED_DATETIME_PATTERN.search(text):
439
444
  return parse_zoned_datetime(match.group(1))
445
+ if match := _ZONED_DATETIME_PATTERN2.search(text):
446
+ return ZonedDateTime.parse_common_iso(match.group(1))
440
447
  if (
441
448
  exc_class := _object_hook_exception_class(
442
449
  text, data=data, objects=objects, redirects=redirects
@@ -753,9 +760,7 @@ class OrjsonFormatter(Formatter):
753
760
  path_name=Path(record.pathname),
754
761
  line_num=record.lineno,
755
762
  message=record.getMessage(),
756
- datetime=dt.datetime.fromtimestamp(
757
- record.created, tz=get_local_time_zone()
758
- ),
763
+ datetime=from_timestamp(record.created, time_zone="local"),
759
764
  func_name=record.funcName,
760
765
  extra=extra if len(extra) >= 1 else None,
761
766
  )
@@ -859,11 +864,16 @@ class GetLogRecordsOutput:
859
864
  for r in self.records
860
865
  ]
861
866
  if len(records) >= 1:
862
- time_zone = one_unique(ensure_time_zone(r.datetime) for r in records)
867
+ time_zone = one_unique(ZoneInfo(r.datetime.tz) for r in records)
863
868
  else:
864
- time_zone = get_local_time_zone()
869
+ time_zone = LOCAL_TIME_ZONE
865
870
  return DataFrame(
866
- data=[dataclass_to_dict(r, recursive=False) for r in records],
871
+ data=[
872
+ dataclass_to_dict(
873
+ replace(r, datetime=r.datetime.py_datetime()), recursive=False
874
+ )
875
+ for r in records
876
+ ],
867
877
  schema={
868
878
  "index": UInt64,
869
879
  "name": String,
@@ -891,9 +901,12 @@ class GetLogRecordsOutput:
891
901
  level: LogLevel | None = None,
892
902
  min_level: LogLevel | None = None,
893
903
  max_level: LogLevel | None = None,
894
- date_or_datetime: DateOrDateTime | None = None,
895
- min_date_or_datetime: DateOrDateTime | None = None,
896
- max_date_or_datetime: DateOrDateTime | None = None,
904
+ date: Date | None = None,
905
+ min_date: Date | None = None,
906
+ max_date: Date | None = None,
907
+ datetime: ZonedDateTime | None = None,
908
+ min_datetime: ZonedDateTime | None = None,
909
+ max_datetime: ZonedDateTime | None = None,
897
910
  func_name: bool | str | None = None,
898
911
  extra: bool | MaybeIterable[str] | None = None,
899
912
  log_file: bool | PathLike | None = None,
@@ -932,30 +945,18 @@ class GetLogRecordsOutput:
932
945
  records = [
933
946
  r for r in records if r.level <= get_logging_level_number(max_level)
934
947
  ]
935
- if date_or_datetime is not None:
936
- match date_or_datetime:
937
- case dt.datetime() as datetime:
938
- records = [r for r in records if r.datetime == datetime]
939
- case dt.date() as date:
940
- records = [r for r in records if r.date == date]
941
- case _ as never:
942
- assert_never(never)
943
- if min_date_or_datetime is not None:
944
- match min_date_or_datetime:
945
- case dt.datetime() as min_datetime:
946
- records = [r for r in records if r.datetime >= min_datetime]
947
- case dt.date() as min_date:
948
- records = [r for r in records if r.date >= min_date]
949
- case _ as never:
950
- assert_never(never)
951
- if max_date_or_datetime is not None:
952
- match max_date_or_datetime:
953
- case dt.datetime() as max_datetime:
954
- records = [r for r in records if r.datetime <= max_datetime]
955
- case dt.date() as max_date:
956
- records = [r for r in records if r.date <= max_date]
957
- case _ as never:
958
- assert_never(never)
948
+ if date is not None:
949
+ records = [r for r in records if r.date == date]
950
+ if min_date is not None:
951
+ records = [r for r in records if r.date >= min_date]
952
+ if max_date is not None:
953
+ records = [r for r in records if r.date <= max_date]
954
+ if datetime is not None:
955
+ records = [r for r in records if r.datetime == datetime]
956
+ if min_datetime is not None:
957
+ records = [r for r in records if r.datetime >= min_datetime]
958
+ if max_datetime is not None:
959
+ records = [r for r in records if r.datetime <= max_datetime]
959
960
  if func_name is not None:
960
961
  match func_name:
961
962
  case bool() as has_func_name:
@@ -1058,7 +1059,7 @@ class OrjsonLogRecord:
1058
1059
  level: int
1059
1060
  path_name: Path
1060
1061
  line_num: int
1061
- datetime: dt.datetime
1062
+ datetime: ZonedDateTime
1062
1063
  func_name: str | None = None
1063
1064
  stack_info: str | None = None
1064
1065
  extra: StrMapping | None = None
@@ -1066,7 +1067,7 @@ class OrjsonLogRecord:
1066
1067
  log_file_line_num: int | None = None
1067
1068
 
1068
1069
  @cached_property
1069
- def date(self) -> dt.date:
1070
+ def date(self) -> Date:
1070
1071
  return self.datetime.date()
1071
1072
 
1072
1073
 
utilities/pyinstrument.py CHANGED
@@ -7,9 +7,8 @@ from typing import TYPE_CHECKING
7
7
  from pyinstrument.profiler import Profiler
8
8
 
9
9
  from utilities.atomicwrites import writer
10
- from utilities.datetime import serialize_compact
11
10
  from utilities.pathlib import get_path
12
- from utilities.tzlocal import get_now_local
11
+ from utilities.whenever2 import format_compact, get_now
13
12
 
14
13
  if TYPE_CHECKING:
15
14
  from collections.abc import Iterator
@@ -23,7 +22,7 @@ def profile(*, path: MaybeCallablePathLike | None = Path.cwd) -> Iterator[None]:
23
22
  with Profiler() as profiler:
24
23
  yield
25
24
  filename = get_path(path=path).joinpath(
26
- f"profile__{serialize_compact(get_now_local())}.html"
25
+ f"profile__{format_compact(get_now())}.html"
27
26
  )
28
27
  with writer(filename) as temp, temp.open(mode="w") as fh:
29
28
  _ = fh.write(profiler.output_html())
utilities/re.py CHANGED
@@ -5,8 +5,6 @@ from dataclasses import dataclass
5
5
  from re import Pattern
6
6
  from typing import TYPE_CHECKING, assert_never, override
7
7
 
8
- from utilities.iterables import OneEmptyError, OneNonUniqueError, one
9
-
10
8
  if TYPE_CHECKING:
11
9
  from utilities.types import PatternLike
12
10
 
@@ -36,16 +34,17 @@ def extract_group(pattern: PatternLike, text: str, /, *, flags: int = 0) -> str:
36
34
  raise _ExtractGroupNoCaptureGroupsError(pattern=pattern_use, text=text)
37
35
  case 1:
38
36
  matches: list[str] = pattern_use.findall(text)
39
- try:
40
- return one(matches)
41
- except OneEmptyError:
42
- raise _ExtractGroupNoMatchesError(
43
- pattern=pattern_use, text=text
44
- ) from None
45
- except OneNonUniqueError:
46
- raise _ExtractGroupMultipleMatchesError(
47
- pattern=pattern_use, text=text, matches=matches
48
- ) from None
37
+ match len(matches):
38
+ case 0:
39
+ raise _ExtractGroupNoMatchesError(
40
+ pattern=pattern_use, text=text
41
+ ) from None
42
+ case 1:
43
+ return matches[0]
44
+ case _:
45
+ raise _ExtractGroupMultipleMatchesError(
46
+ pattern=pattern_use, text=text, matches=matches
47
+ ) from None
49
48
  case _:
50
49
  raise _ExtractGroupMultipleCaptureGroupsError(
51
50
  pattern=pattern_use, text=text
@@ -109,7 +108,7 @@ def extract_groups(pattern: PatternLike, text: str, /, *, flags: int = 0) -> lis
109
108
  case 1, 1:
110
109
  return matches
111
110
  case 1, _:
112
- return list(one(matches))
111
+ return list(matches[0])
113
112
  case _:
114
113
  raise _ExtractGroupsMultipleMatchesError(
115
114
  pattern=pattern_use, text=text, matches=matches
utilities/traceback.py CHANGED
@@ -14,7 +14,6 @@ from traceback import TracebackException
14
14
  from typing import TYPE_CHECKING, override
15
15
 
16
16
  from utilities.atomicwrites import writer
17
- from utilities.datetime import get_datetime, get_now, serialize_compact
18
17
  from utilities.errors import repr_error
19
18
  from utilities.iterables import OneEmptyError, one
20
19
  from utilities.pathlib import get_path
@@ -27,16 +26,19 @@ from utilities.reprlib import (
27
26
  RICH_MAX_WIDTH,
28
27
  yield_mapping_repr,
29
28
  )
30
- from utilities.tzlocal import get_local_time_zone, get_now_local
31
29
  from utilities.version import get_version
32
- from utilities.whenever import serialize_duration, serialize_zoned_datetime
30
+ from utilities.whenever2 import format_compact, get_now, to_zoned_date_time
33
31
 
34
32
  if TYPE_CHECKING:
35
33
  from collections.abc import Callable, Iterator, Sequence
36
34
  from traceback import FrameSummary
37
35
  from types import TracebackType
38
36
 
39
- from utilities.types import MaybeCallableDateTime, MaybeCallablePathLike, PathLike
37
+ from utilities.types import (
38
+ MaybeCallablePathLike,
39
+ MaybeCallableZonedDateTime,
40
+ PathLike,
41
+ )
40
42
  from utilities.version import MaybeCallableVersionLike
41
43
 
42
44
 
@@ -51,7 +53,7 @@ def format_exception_stack(
51
53
  /,
52
54
  *,
53
55
  header: bool = False,
54
- start: MaybeCallableDateTime | None = _START,
56
+ start: MaybeCallableZonedDateTime | None = _START,
55
57
  version: MaybeCallableVersionLike | None = None,
56
58
  capture_locals: bool = False,
57
59
  max_width: int = RICH_MAX_WIDTH,
@@ -82,21 +84,18 @@ def format_exception_stack(
82
84
 
83
85
  def _yield_header_lines(
84
86
  *,
85
- start: MaybeCallableDateTime | None = _START,
87
+ start: MaybeCallableZonedDateTime | None = _START,
86
88
  version: MaybeCallableVersionLike | None = None,
87
89
  ) -> Iterator[str]:
88
90
  """Yield the header lines."""
89
- now = get_now_local()
90
- start_use = get_datetime(datetime=start)
91
- start_use = (
92
- None if start_use is None else start_use.astimezone(get_local_time_zone())
93
- )
94
- yield f"Date/time | {serialize_zoned_datetime(now)}"
95
- start_str = "" if start_use is None else serialize_zoned_datetime(start_use)
91
+ now = get_now()
92
+ start_use = to_zoned_date_time(date_time=start)
93
+ yield f"Date/time | {format_compact(now)}"
94
+ start_str = "" if start_use is None else format_compact(start_use)
96
95
  yield f"Started | {start_str}"
97
- duration = None if start_use is None else (now - start_use)
98
- duration_str = "" if duration is None else serialize_duration(duration)
99
- yield f"Duration | {duration_str}"
96
+ delta = None if start_use is None else (now - start_use)
97
+ delta_str = "" if delta is None else delta.format_common_iso()
98
+ yield f"Duration | {delta_str}"
100
99
  yield f"User | {getuser()}"
101
100
  yield f"Host | {gethostname()}"
102
101
  version_use = "" if version is None else get_version(version=version)
@@ -193,7 +192,7 @@ def _trim_path(path: PathLike, pattern: str, /) -> Path | None:
193
192
 
194
193
  def make_except_hook(
195
194
  *,
196
- start: MaybeCallableDateTime | None = _START,
195
+ start: MaybeCallableZonedDateTime | None = _START,
197
196
  version: MaybeCallableVersionLike | None = None,
198
197
  path: MaybeCallablePathLike | None = None,
199
198
  max_width: int = RICH_MAX_WIDTH,
@@ -228,7 +227,7 @@ def _make_except_hook_inner(
228
227
  traceback: TracebackType | None,
229
228
  /,
230
229
  *,
231
- start: MaybeCallableDateTime | None = _START,
230
+ start: MaybeCallableZonedDateTime | None = _START,
232
231
  version: MaybeCallableVersionLike | None = None,
233
232
  path: MaybeCallablePathLike | None = None,
234
233
  max_width: int = RICH_MAX_WIDTH,
@@ -247,9 +246,7 @@ def _make_except_hook_inner(
247
246
  _ = sys.stderr.write(f"{slim}\n") # don't 'from sys import stderr'
248
247
  if path is not None:
249
248
  path = (
250
- get_path(path=path)
251
- .joinpath(serialize_compact(get_now_local()))
252
- .with_suffix(".txt")
249
+ get_path(path=path).joinpath(format_compact(get_now())).with_suffix(".txt")
253
250
  )
254
251
  full = format_exception_stack(
255
252
  exc_val,
utilities/types.py CHANGED
@@ -20,6 +20,16 @@ from typing import (
20
20
  )
21
21
  from zoneinfo import ZoneInfo
22
22
 
23
+ from whenever import (
24
+ Date,
25
+ DateDelta,
26
+ DateTimeDelta,
27
+ PlainDateTime,
28
+ Time,
29
+ TimeDelta,
30
+ ZonedDateTime,
31
+ )
32
+
23
33
  _T_co = TypeVar("_T_co", covariant=True)
24
34
  _T_contra = TypeVar("_T_contra", contravariant=True)
25
35
 
@@ -88,17 +98,16 @@ TDataclass = TypeVar("TDataclass", bound=Dataclass)
88
98
 
89
99
 
90
100
  # datetime
91
- type DateLike = MaybeStr[dt.date]
92
- type DateTimeLike = MaybeStr[dt.datetime]
93
101
  type DateOrDateTime = dt.date | dt.datetime
102
+ type DateTimeLike = MaybeStr[dt.datetime]
94
103
  type Duration = Number | dt.timedelta
95
104
  type DurationLike = MaybeStr[Duration]
96
105
  type DurationOrEveryDuration = Duration | tuple[Literal["every"], Duration]
97
- type MaybeCallableDate = MaybeCallable[dt.date]
98
- type MaybeCallableDateTime = MaybeCallable[dt.datetime]
99
- type TimeLike = MaybeStr[dt.time]
100
- type TimeDeltaLike = MaybeStr[dt.timedelta]
101
- type WeekDay = Literal["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
106
+ type MaybeCallablePyDate = MaybeCallable[dt.date]
107
+ type MaybeCallablePyDateTime = MaybeCallable[dt.datetime]
108
+ type PyDateLike = MaybeStr[dt.date]
109
+ type PyTimeDeltaLike = MaybeStr[dt.timedelta]
110
+ type PyTimeLike = MaybeStr[dt.time]
102
111
 
103
112
 
104
113
  # enum
@@ -133,7 +142,7 @@ type LoggerOrName = MaybeStr[Logger]
133
142
 
134
143
  # math
135
144
  type Number = int | float
136
- type RoundMode = Literal[
145
+ type MathRoundMode = Literal[
137
146
  "standard",
138
147
  "floor",
139
148
  "ceil",
@@ -259,6 +268,25 @@ type ExcInfo = tuple[type[BaseException], BaseException, TracebackType]
259
268
  type OptExcInfo = ExcInfo | tuple[None, None, None]
260
269
 
261
270
 
271
+ # whenever
272
+ type DateDeltaLike = MaybeStr[DateDelta]
273
+ type DateLike = MaybeStr[Date]
274
+ type DateTimeDeltaLike = MaybeStr[DateTimeDelta]
275
+ type MaybeCallableDate = MaybeCallable[Date]
276
+ type MaybeCallableZonedDateTime = MaybeCallable[ZonedDateTime]
277
+ type PlainDateTimeLike = MaybeStr[PlainDateTime]
278
+ type TimeDeltaLike = MaybeStr[TimeDelta]
279
+ type TimeLike = MaybeStr[Time]
280
+ type ZonedDateTimeLike = MaybeStr[ZonedDateTime]
281
+ type DateTimeRoundUnit = Literal[
282
+ "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"
283
+ ]
284
+ type DateTimeRoundMode = Literal[
285
+ "ceil", "floor", "half_ceil", "half_floor", "half_even"
286
+ ]
287
+ type WeekDay = Literal["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
288
+
289
+
262
290
  # zoneinfo
263
291
  # fmt: off
264
292
  type TimeZone = Literal[
@@ -271,9 +299,13 @@ type TimeZoneLike = ZoneInfo | Literal["local"] | TimeZone | dt.tzinfo | dt.date
271
299
  __all__ = [
272
300
  "Coroutine1",
273
301
  "Dataclass",
302
+ "DateDeltaLike",
274
303
  "DateLike",
275
304
  "DateOrDateTime",
305
+ "DateTimeDeltaLike",
276
306
  "DateTimeLike",
307
+ "DateTimeRoundMode",
308
+ "DateTimeRoundUnit",
277
309
  "Duration",
278
310
  "DurationLike",
279
311
  "DurationOrEveryDuration",
@@ -282,12 +314,15 @@ __all__ = [
282
314
  "IterableHashable",
283
315
  "LogLevel",
284
316
  "LoggerOrName",
317
+ "MathRoundMode",
285
318
  "MaybeAwaitable",
286
319
  "MaybeCallable",
287
320
  "MaybeCallableDate",
288
- "MaybeCallableDateTime",
289
321
  "MaybeCallableEvent",
290
322
  "MaybeCallablePathLike",
323
+ "MaybeCallablePyDate",
324
+ "MaybeCallablePyDateTime",
325
+ "MaybeCallableZonedDateTime",
291
326
  "MaybeCoroutine1",
292
327
  "MaybeIterable",
293
328
  "MaybeIterableHashable",
@@ -300,7 +335,10 @@ __all__ = [
300
335
  "ParseObjectExtra",
301
336
  "PathLike",
302
337
  "PatternLike",
303
- "RoundMode",
338
+ "PlainDateTimeLike",
339
+ "PyDateLike",
340
+ "PyTimeDeltaLike",
341
+ "PyTimeLike",
304
342
  "Seed",
305
343
  "SerializeObjectExtra",
306
344
  "Sign",
@@ -343,4 +381,5 @@ __all__ = [
343
381
  "TupleOrStrMapping",
344
382
  "TypeLike",
345
383
  "WeekDay",
384
+ "ZonedDateTimeLike",
346
385
  ]
utilities/typing.py CHANGED
@@ -25,6 +25,16 @@ from typing import get_type_hints as _get_type_hints
25
25
  from uuid import UUID
26
26
  from warnings import warn
27
27
 
28
+ from whenever import (
29
+ Date,
30
+ DateDelta,
31
+ DateTimeDelta,
32
+ PlainDateTime,
33
+ Time,
34
+ TimeDelta,
35
+ ZonedDateTime,
36
+ )
37
+
28
38
  from utilities.iterables import unique_everseen
29
39
  from utilities.sentinel import Sentinel
30
40
  from utilities.types import StrMapping
@@ -133,7 +143,21 @@ def get_type_hints(
133
143
  ) -> dict[str, Any]:
134
144
  """Get the type hints of an object."""
135
145
  result: dict[str, Any] = obj.__annotations__
136
- _ = {Literal, Path, Sentinel, StrMapping, UUID, dt}
146
+ _ = {
147
+ Date,
148
+ DateDelta,
149
+ DateTimeDelta,
150
+ Literal,
151
+ Path,
152
+ PlainDateTime,
153
+ Sentinel,
154
+ StrMapping,
155
+ Time,
156
+ TimeDelta,
157
+ UUID,
158
+ ZonedDateTime,
159
+ dt,
160
+ }
137
161
  globalns_use = globals() | ({} if globalns is None else dict(globalns))
138
162
  localns_use = {} if localns is None else dict(localns)
139
163
  try:
utilities/tzlocal.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import datetime as dt
4
3
  from logging import getLogger
5
4
  from typing import TYPE_CHECKING
6
5
 
@@ -21,30 +20,7 @@ def get_local_time_zone() -> ZoneInfo:
21
20
 
22
21
 
23
22
  LOCAL_TIME_ZONE = get_local_time_zone()
23
+ LOCAL_TIME_ZONE_NAME = LOCAL_TIME_ZONE.key
24
24
 
25
25
 
26
- def get_now_local() -> dt.datetime:
27
- """Get the current local time."""
28
- return dt.datetime.now(tz=LOCAL_TIME_ZONE)
29
-
30
-
31
- NOW_LOCAL = get_now_local()
32
-
33
-
34
- def get_today_local() -> dt.date:
35
- """Get the current, timezone-aware local date."""
36
- return get_now_local().date()
37
-
38
-
39
- TODAY_LOCAL = get_today_local()
40
-
41
-
42
- __all__ = [
43
- "LOCAL_TIME_ZONE",
44
- "LOCAL_TIME_ZONE",
45
- "NOW_LOCAL",
46
- "TODAY_LOCAL",
47
- "get_local_time_zone",
48
- "get_now_local",
49
- "get_today_local",
50
- ]
26
+ __all__ = ["LOCAL_TIME_ZONE", "LOCAL_TIME_ZONE_NAME", "get_local_time_zone"]
utilities/whenever.py CHANGED
@@ -34,12 +34,12 @@ from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
34
34
 
35
35
  if TYPE_CHECKING:
36
36
  from utilities.types import (
37
- DateLike,
38
37
  DateTimeLike,
39
38
  Duration,
40
39
  DurationLike,
41
- TimeDeltaLike,
42
- TimeLike,
40
+ PyDateLike,
41
+ PyTimeDeltaLike,
42
+ PyTimeLike,
43
43
  )
44
44
 
45
45
 
@@ -91,7 +91,7 @@ class _CheckValidZonedDateTimeUnequalError(CheckValidZonedDateTimeError):
91
91
  ##
92
92
 
93
93
 
94
- def ensure_date(date: DateLike, /) -> dt.date:
94
+ def ensure_date(date: PyDateLike, /) -> dt.date:
95
95
  """Ensure the object is a date."""
96
96
  if isinstance(date, dt.date):
97
97
  check_date_not_datetime(date)
@@ -180,7 +180,7 @@ class EnsurePlainDateTimeError(Exception):
180
180
  ##
181
181
 
182
182
 
183
- def ensure_time(time: TimeLike, /) -> dt.time:
183
+ def ensure_time(time: PyTimeLike, /) -> dt.time:
184
184
  """Ensure the object is a time."""
185
185
  if isinstance(time, dt.time):
186
186
  return time
@@ -202,7 +202,7 @@ class EnsureTimeError(Exception):
202
202
  ##
203
203
 
204
204
 
205
- def ensure_timedelta(timedelta: TimeDeltaLike, /) -> dt.timedelta:
205
+ def ensure_timedelta(timedelta: PyTimeDeltaLike, /) -> dt.timedelta:
206
206
  """Ensure the object is a timedelta."""
207
207
  if isinstance(timedelta, dt.timedelta):
208
208
  return timedelta