Skip to content

Commit 7fe1fc9

Browse files
authored
Make string_to_timedelta more robust
1 parent 1383857 commit 7fe1fc9

1 file changed

Lines changed: 29 additions & 4 deletions

File tree

src/synoptic/services.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,44 @@ def string_to_timedelta(x: str) -> timedelta:
7272
"""
7373
Parse a duration string to a timedelta.
7474
75+
Parameters
76+
----------
7577
x : str
7678
String representation of duration. Can use ISO 8601 duration, in
7779
a limited fashion, or a Polars-style period string.
7880
- `'PT30M'` = 30 minutes
7981
- `'P1DT6H'` = 1 day, 6 hours
8082
- `'30m'` = 30 minutes
8183
- `'3d6h30m10s'` = 3 days, 6 hours, 30 minutes, 10 seconds
84+
85+
Returns
86+
-------
87+
timedelta
88+
The parsed timedelta object.
89+
90+
Raises
91+
------
92+
ValueError
93+
If the string cannot be parsed as a valid duration.
8294
"""
83-
x = x.lower()
95+
x = x.strip().lower()
8496
x = x.replace("p", "").replace("t", "")
85-
pattern = r"(?:(?P<days>\d+)d)?(?:(?P<hours>\d+)h)?(?:(?P<minutes>\d+)m)?(?:(?P<seconds>\d+)s)?"
86-
87-
groups = re.match(pattern, x).groupdict()
97+
98+
pattern = r"^(?:(?P<days>\d+)d)?(?:(?P<hours>\d+)h)?(?:(?P<minutes>\d+)m)?(?:(?P<seconds>\d+)s)?$"
99+
100+
match = re.match(pattern, x)
101+
if not match:
102+
raise ValueError(
103+
f"Invalid duration string: '{x}'. "
104+
"Expected format like '3d6h30m10s' or 'PT30M'"
105+
)
106+
107+
groups = match.groupdict()
108+
109+
# Check if at least one time component was provided
110+
if all(v is None for v in groups.values()):
111+
raise ValueError(f"No time components found in duration string: '{x}'")
112+
88113
kwargs = {k: int(v) for k, v in groups.items() if v is not None}
89114
return timedelta(**kwargs)
90115

0 commit comments

Comments
 (0)