Skip to content

Commit c8dffd9

Browse files
author
Rodrigo Roldán
committed
feat: add delta utility properties and constructors
1 parent 6252687 commit c8dffd9

4 files changed

Lines changed: 70 additions & 1 deletion

File tree

src/eones/core/delta.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,16 @@ def scale(self, factor: int) -> "Delta":
177177
"""
178178
return Delta(**{k: v * factor for k, v in self.to_input_dict().items()})
179179

180+
@property
181+
def total_months(self) -> int:
182+
"""Return the calendar component expressed as total months."""
183+
return self._calendar.total_months
184+
185+
@property
186+
def total_seconds(self) -> int:
187+
"""Return the duration component expressed as total seconds."""
188+
return self._duration.total_seconds
189+
180190
def is_zero(self) -> bool:
181191
"""
182192
Check if the delta has no effect.
@@ -263,3 +273,14 @@ def from_iso(cls, iso: str) -> "Delta":
263273
raise ValueError(f"Invalid ISO delta: {iso}")
264274
parts = {k: int(v) for k, v in match.groupdict().items() if v is not None}
265275
return cls(**parts)
276+
277+
@classmethod
278+
def from_timedelta(cls, td: "timedelta") -> "Delta":
279+
"""Create a Delta instance from :class:`datetime.timedelta`."""
280+
from datetime import timedelta
281+
282+
if not isinstance(td, timedelta):
283+
raise TypeError(f"'td' must be timedelta, got {type(td).__name__}")
284+
285+
duration = DeltaDuration.from_timedelta(td)
286+
return cls(**duration.to_input_dict())

src/eones/core/delta_calendar.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ def scale(self, factor: int) -> "DeltaCalendar":
9090
items = self.to_input_dict().items()
9191
return DeltaCalendar(**{k: v * factor for k, v in items})
9292

93+
@property
94+
def total_months(self) -> int:
95+
"""Return the delta expressed as total months."""
96+
return self.years * 12 + self.months
97+
9398
def to_dict(self) -> Dict[str, int]:
9499
"""
95100
Return a normalized version of the delta.

src/eones/core/delta_duration.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ def scale(self, factor: int) -> "DeltaDuration":
105105
**{key: value * factor for key, value in self._input.items()}
106106
)
107107

108+
@property
109+
def total_seconds(self) -> int:
110+
"""Return the duration expressed in total seconds."""
111+
return int(self.timedelta.total_seconds())
112+
108113
def is_zero(self) -> bool:
109114
"""
110115
Check if the duration is equivalent to zero.
@@ -144,6 +149,29 @@ def to_iso(self) -> str:
144149

145150
return result or "P0D"
146151

152+
@classmethod
153+
def from_timedelta(cls, td: timedelta) -> "DeltaDuration":
154+
"""Create a DeltaDuration from a :class:`datetime.timedelta`."""
155+
if not isinstance(td, timedelta):
156+
raise TypeError(f"'td' must be timedelta, got {type(td).__name__}")
157+
158+
total_seconds = int(td.total_seconds())
159+
sign = -1 if total_seconds < 0 else 1
160+
total_seconds = abs(total_seconds)
161+
162+
days, rem = divmod(total_seconds, 86400)
163+
weeks, days = divmod(days, 7)
164+
hours, rem = divmod(rem, 3600)
165+
minutes, seconds = divmod(rem, 60)
166+
167+
return cls(
168+
weeks=weeks * sign,
169+
days=days * sign,
170+
hours=hours * sign,
171+
minutes=minutes * sign,
172+
seconds=seconds * sign,
173+
)
174+
147175
@classmethod
148176
def from_iso(cls, iso: str) -> "DeltaDuration":
149177
"""

tests/test_delta.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, timedelta
22
from zoneinfo import ZoneInfo
33

44
import pytest
@@ -245,3 +245,18 @@ def test_from_iso_time_only():
245245
def test_from_iso_invalid():
246246
with pytest.raises(ValueError):
247247
Delta.from_iso("XYZ")
248+
249+
250+
def test_total_properties_and_from_timedelta():
251+
td = timedelta(days=1, hours=2, minutes=3, seconds=4)
252+
delta = Delta.from_timedelta(td)
253+
assert delta.to_input_dict() == {
254+
"days": 1,
255+
"hours": 2,
256+
"minutes": 3,
257+
"seconds": 4,
258+
}
259+
260+
combined = Delta(years=1, months=2, days=1, hours=1)
261+
assert combined.total_months == 14
262+
assert combined.total_seconds == timedelta(days=1, hours=1).total_seconds()

0 commit comments

Comments
 (0)