dycw-utilities 0.150.10__py3-none-any.whl → 0.150.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dycw-utilities
3
- Version: 0.150.10
3
+ Version: 0.150.12
4
4
  Author-email: Derek Wan <d.wan@icloud.com>
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -12,7 +12,7 @@ Provides-Extra: logging
12
12
  Requires-Dist: coloredlogs<15.1,>=15.0.1; extra == 'logging'
13
13
  Provides-Extra: test
14
14
  Requires-Dist: dycw-pytest-only<2.2,>=2.1.1; extra == 'test'
15
- Requires-Dist: hypothesis<6.137,>=6.136.0; extra == 'test'
15
+ Requires-Dist: hypothesis<6.137,>=6.136.1; extra == 'test'
16
16
  Requires-Dist: pudb<2025.2,>=2025.1; extra == 'test'
17
17
  Requires-Dist: pytest-asyncio<1.2,>=1.1.0; extra == 'test'
18
18
  Requires-Dist: pytest-cov<6.3,>=6.2.1; extra == 'test'
@@ -1,4 +1,4 @@
1
- utilities/__init__.py,sha256=z4XmznKj7QSMhMSr917kvFx3G8dxMQTFwkCq9kXjV8k,61
1
+ utilities/__init__.py,sha256=f0cXpht3DHjUGXeay5K4fSvNR_jeHgGWtdP8z2h1Hws,61
2
2
  utilities/altair.py,sha256=92E2lCdyHY4Zb-vCw6rEJIsWdKipuu-Tu2ab1ufUfAk,9079
3
3
  utilities/asyncio.py,sha256=2m2a2C-Qgc6OHTTHL332-t66A7xDITt_SORT7a1DJWo,16792
4
4
  utilities/atomicwrites.py,sha256=xcOWenTBRS0oat3kg7Sqe51AohNThMQ2ixPL7QCG8hw,5795
@@ -44,7 +44,7 @@ utilities/orjson.py,sha256=iJho5gCkTlT2KWSXqopvi24WCxEhM5_fiz17mbNvkGg,40113
44
44
  utilities/os.py,sha256=y25oqJoyrkdQ7aU5ShePPXKk6YTwd97x6yEx4UcRYu4,3788
45
45
  utilities/parse.py,sha256=JcJn5yXKhIWXBCwgBdPsyu7Hvcuw6kyEdqvaebCaI9k,17951
46
46
  utilities/pathlib.py,sha256=FnteXeVeMOSc6QTN7oF6UrobjOX9gXv_5tG1slg83W8,8496
47
- utilities/period.py,sha256=REuz2xaChNa5Iw69xNnbUPlUSLdUgmhOu4YEOiPeTrg,8328
47
+ utilities/period.py,sha256=hsHdAKAstfMzB2Ar5EbxjkbMff3CA-B5wtYNVZOXVXI,10127
48
48
  utilities/pickle.py,sha256=MBT2xZCsv0pH868IXLGKnlcqNx2IRVKYNpRcqiQQqxw,653
49
49
  utilities/platform.py,sha256=Ue9LSxYvg9yUXGKuz5aZoy_qkUEXde-v6B09exgSctU,2813
50
50
  utilities/polars.py,sha256=BgiDryAVOapi41ddfJqN0wYh_sDj8BNEYtPB36LaHdo,71824
@@ -89,8 +89,8 @@ utilities/zoneinfo.py,sha256=oEH-nL3t4h9uawyZqWDtNtDAl6M-CLpLYGI_nI6DulM,1971
89
89
  utilities/pytest_plugins/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
90
90
  utilities/pytest_plugins/pytest_randomly.py,sha256=NXzCcGKbpgYouz5yehKb4jmxmi2SexKKpgF4M65bi10,414
91
91
  utilities/pytest_plugins/pytest_regressions.py,sha256=Iwhfv_OJH7UCPZCfoh7ugZ2Xjqjil-BBBsOb8sDwiGI,1471
92
- dycw_utilities-0.150.10.dist-info/METADATA,sha256=I9hOdk7uXEH7C06E8LpFpvC7Herkzgmtz4qg9UulEcY,1697
93
- dycw_utilities-0.150.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
- dycw_utilities-0.150.10.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
95
- dycw_utilities-0.150.10.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
- dycw_utilities-0.150.10.dist-info/RECORD,,
92
+ dycw_utilities-0.150.12.dist-info/METADATA,sha256=y8XRkRo0g6VjLy7bzDQOl0cu0H6547VnzBxSAaso5yA,1697
93
+ dycw_utilities-0.150.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
+ dycw_utilities-0.150.12.dist-info/entry_points.txt,sha256=BOD_SoDxwsfJYOLxhrSXhHP_T7iw-HXI9f2WVkzYxvQ,135
95
+ dycw_utilities-0.150.12.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
96
+ dycw_utilities-0.150.12.dist-info/RECORD,,
utilities/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.150.10"
3
+ __version__ = "0.150.12"
utilities/period.py CHANGED
@@ -1,22 +1,22 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Self, TypedDict, overload, override
4
+ from typing import TYPE_CHECKING, Any, Self, TypedDict, assert_never, overload, override
5
5
  from zoneinfo import ZoneInfo
6
6
 
7
- from whenever import Date, DateDelta, PlainDateTime, TimeDelta, ZonedDateTime
7
+ from whenever import Date, DateDelta, PlainDateTime, Time, TimeDelta, ZonedDateTime
8
8
 
9
9
  from utilities.dataclasses import replace_non_sentinel
10
10
  from utilities.functions import get_class_name
11
11
  from utilities.sentinel import Sentinel, sentinel
12
12
  from utilities.whenever import format_compact
13
- from utilities.zoneinfo import get_time_zone_name
13
+ from utilities.zoneinfo import UTC, ensure_time_zone, get_time_zone_name
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  from utilities.types import TimeZoneLike
17
17
 
18
18
 
19
- class _PeriodAsDict[T: (Date, ZonedDateTime)](TypedDict):
19
+ class _PeriodAsDict[T: (Date, Time, ZonedDateTime)](TypedDict):
20
20
  start: T
21
21
  end: T
22
22
 
@@ -49,6 +49,22 @@ class DatePeriod:
49
49
  """Offset the period."""
50
50
  return self.replace(start=self.start - other, end=self.end - other)
51
51
 
52
+ def at(
53
+ self, obj: Time | tuple[Time, Time], /, *, time_zone: TimeZoneLike = UTC
54
+ ) -> ZonedDateTimePeriod:
55
+ """Combine a date with a time to create a datetime."""
56
+ match obj:
57
+ case Time() as time:
58
+ start = end = time
59
+ case Time() as start, Time() as end:
60
+ ...
61
+ case _ as never:
62
+ assert_never(never)
63
+ tz = ensure_time_zone(time_zone).key
64
+ return ZonedDateTimePeriod(
65
+ self.start.at(start).assume_tz(tz), self.end.at(end).assume_tz(tz)
66
+ )
67
+
52
68
  @property
53
69
  def delta(self) -> DateDelta:
54
70
  """The delta of the period."""
@@ -76,6 +92,42 @@ class DatePeriod:
76
92
  return _PeriodAsDict(start=self.start, end=self.end)
77
93
 
78
94
 
95
+ @dataclass(repr=False, order=True, unsafe_hash=True, kw_only=False)
96
+ class TimePeriod:
97
+ """A period of times."""
98
+
99
+ start: Time
100
+ end: Time
101
+
102
+ @override
103
+ def __repr__(self) -> str:
104
+ cls = get_class_name(self)
105
+ return f"{cls}({self.start}, {self.end})"
106
+
107
+ def replace(
108
+ self, *, start: Time | Sentinel = sentinel, end: Time | Sentinel = sentinel
109
+ ) -> Self:
110
+ """Replace elements of the period."""
111
+ return replace_non_sentinel(self, start=start, end=end)
112
+
113
+ def at(
114
+ self, obj: Date | tuple[Date, Date], /, *, time_zone: TimeZoneLike = UTC
115
+ ) -> ZonedDateTimePeriod:
116
+ """Combine a date with a time to create a datetime."""
117
+ match obj:
118
+ case Date() as date:
119
+ start = end = date
120
+ case Date() as start, Date() as end:
121
+ ...
122
+ case _ as never:
123
+ assert_never(never)
124
+ return DatePeriod(start, end).at((self.start, self.end), time_zone=time_zone)
125
+
126
+ def to_dict(self) -> _PeriodAsDict[Time]:
127
+ """Convert the period to a dictionary."""
128
+ return _PeriodAsDict(start=self.start, end=self.end)
129
+
130
+
79
131
  @dataclass(repr=False, order=True, unsafe_hash=True, kw_only=False)
80
132
  class ZonedDateTimePeriod:
81
133
  """A period of time."""
@@ -234,4 +286,4 @@ class _PeriodExactEqArgumentsError(PeriodError):
234
286
  return f"Invalid arguments; got {self.args}"
235
287
 
236
288
 
237
- __all__ = ["DatePeriod", "PeriodError", "ZonedDateTimePeriod"]
289
+ __all__ = ["DatePeriod", "PeriodError", "TimePeriod", "ZonedDateTimePeriod"]