Files

41 lines
1.4 KiB
Python

import re
from datetime import timedelta
DURATION_RE = re.compile(r"^(?:\d+[dhm])+$|^\d+$", re.IGNORECASE)
PART_RE = re.compile(r"(\d+)([dhm])", re.IGNORECASE)
DIGITS_MAX = 6
UNIT_MINUTES = {"d": 1440, "h": 60, "m": 1}
class DurationError(ValueError):
pass
def looks_like_duration(token: str) -> bool:
return bool(DURATION_RE.match(token))
def parse_duration(token: str, maximum: timedelta) -> timedelta:
if not looks_like_duration(token):
raise DurationError("Dauer nicht erkannt. Beispiele: 30m, 2h, 1d, 1h30m, 90 (Minuten).")
if any(len(number) > DIGITS_MAX for number in re.findall(r"\d+", token)):
raise DurationError("Dauer ist zu groß.")
if token.isdecimal():
minutes = int(token)
else:
minutes = sum(int(n) * UNIT_MINUTES[u.lower()] for n, u in PART_RE.findall(token))
if minutes <= 0:
raise DurationError("Die Dauer muss größer als null sein.")
duration = timedelta(minutes=minutes)
if duration > maximum:
raise DurationError(f"Die Dauer ist zu lang, erlaubt sind maximal {format_duration(maximum)}.")
return duration
def format_duration(duration: timedelta) -> str:
total = int(duration.total_seconds() // 60)
days, rest = divmod(total, 1440)
hours, minutes = divmod(rest, 60)
parts = [f"{value} {unit}" for value, unit in ((days, "d"), (hours, "h"), (minutes, "min")) if value]
return " ".join(parts) or "0 min"