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

@ -166,9 +166,39 @@ class TestParseDateArg:
with pytest.raises(ValueError):
self._parse("13-32")
def test_unrecognised_format(self):
with pytest.raises(ValueError, match="Unrecognised"):
parse_date_arg("not-a-date")
def test_unrecognised_format_falls_back_to_natural(self):
# A value that looks nothing like a date should raise via _parse_natural
with pytest.raises(ValueError):
parse_date_arg("not-a-date-at-all-xyz")
def test_whitespace_stripped(self):
assert parse_date_arg(" 2026-05-22 ") == date(2026, 5, 22)
class TestParseDateArgNatural:
"""Tests for natural language fallback via dateparser."""
def test_today(self):
from datetime import date as date_cls
result = parse_date_arg("today")
assert result == date_cls.today()
def test_yesterday(self):
from datetime import date as date_cls
from datetime import timedelta
result = parse_date_arg("yesterday")
assert result == date_cls.today() - timedelta(days=1)
def test_weekday_name(self):
from datetime import date as date_cls
result = parse_date_arg("monday")
# Must be a Monday and not in the future
assert result.weekday() == 0
assert result <= date_cls.today()
def test_garbage_raises(self):
with pytest.raises(ValueError):
parse_date_arg("not-a-date-at-all-xyz")