feat(cli): add natural language date parsing via dateparser

- Add _parse_natural() to utils.py using dateparser as a fallback when
  structured date formats (YYYY-MM-DD, MM-DD, DD-MM) don't match
- Supports expressions like 'today', 'yesterday', 'monday', '3 days ago'
- Change day argument to nargs='*' and join tokens so unquoted
  multi-word expressions like: uv run timesheets 3 days ago work correctly
- Pin dateparser to English to avoid locale-dependent behaviour
- Update tests to cover natural language cases and fix test_last_monday
  (dateparser does not support 'last monday'; use 'monday' instead)
This commit is contained in:
Jef Roosens 2026-05-22 10:57:16 +02:00
parent 29698b1241
commit 615bfe30e0
Signed by: Jef Roosens
GPG key ID: 119385BCAA005C21
6 changed files with 207 additions and 13 deletions

View file

@ -63,10 +63,29 @@ def parse_date_arg(value: str) -> date:
raise ValueError(f"Invalid date: {value!r}")
return result
raise ValueError(
f"Unrecognised date format: {value!r}. "
"Expected YYYY-MM-DD, MM-DD, or DD-MM (- or / as separator)."
# Nothing matched structured formats — try natural language
return _parse_natural(value)
def _parse_natural(value: str) -> date:
"""
Parse a natural language date string using dateparser.
Raises ValueError if the string cannot be interpreted.
"""
import dateparser
result = dateparser.parse(
value,
languages=["en"],
settings={"PREFER_DATES_FROM": "past", "RETURN_AS_TIMEZONE_AWARE": False},
)
if result is None:
raise ValueError(
f"Could not interpret {value!r} as a date. "
"Try a structured format like '2026-05-22' or '05-22', "
"or a relative expression like 'today', 'yesterday', 'monday', or '3 days ago'."
)
return result.date()
def parse_duration(duration_str: str) -> float: