"""Unit tests for the lightweight natural-language time parser."""
from datetime import datetime, timedelta, timezone
import pytest
from navi.tools._internal.time_parser import parse_when
class TestParseIso:
def test_iso_datetime(self):
dt, err = parse_when("2026-05-20T14:30:00")
assert err is None
assert dt == datetime(2026, 5, 20, 14, 30, 0, tzinfo=timezone.utc)
def test_iso_datetime_with_z(self):
dt, err = parse_when("2026-05-20T14:30:00Z")
assert err is None
assert dt == datetime(2026, 5, 20, 14, 30, 0, tzinfo=timezone.utc)
def test_iso_datetime_with_offset(self):
dt, err = parse_when("2026-05-20T14:30:00+03:00")
assert err is None
assert dt is not None
assert dt.utcoffset().total_seconds() == 10800
class TestParseRelative:
def test_relative_days_hours(self):
dt, err = parse_when("2d 6h")
assert err is None
expected = datetime.now(timezone.utc) + timedelta(days=2, hours=6)
assert abs((dt - expected).total_seconds()) < 5
def test_relative_in_hours(self):
dt, err = parse_when("in 3 hours")
assert err is None
expected = datetime.now(timezone.utc) + timedelta(hours=3)
assert abs((dt - expected).total_seconds()) < 5
def test_relative_in_minutes(self):
dt, err = parse_when("in 30 minutes")
assert err is None
expected = datetime.now(timezone.utc) + timedelta(minutes=30)
assert abs((dt - expected).total_seconds()) < 5
def test_relative_compact_minutes(self):
dt, err = parse_when("30m")
assert err is None
expected = datetime.now(timezone.utc) + timedelta(minutes=30)
assert abs((dt - expected).total_seconds()) < 5
def test_relative_compact_seconds(self):
dt, err = parse_when("90s")
assert err is None
expected = datetime.now(timezone.utc) + timedelta(seconds=90)
assert abs((dt - expected).total_seconds()) < 5
def test_relative_mixed_compact(self):
dt, err = parse_when("1d 2h 30m")
assert err is None
expected = datetime.now(timezone.utc) + timedelta(days=1, hours=2, minutes=30)
assert abs((dt - expected).total_seconds()) < 5
class TestParseTomorrow:
def test_tomorrow_at_time(self):
dt, err = parse_when("tomorrow at 09:00")
assert err is None
now = datetime.now(timezone.utc)
assert dt.date() == (now + timedelta(days=1)).date()
assert dt.hour == 9
assert dt.minute == 0
def test_tomorrow_without_at(self):
dt, err = parse_when("tomorrow 14:30")
assert err is None
now = datetime.now(timezone.utc)
assert dt.date() == (now + timedelta(days=1)).date()
assert dt.hour == 14
assert dt.minute == 30
class TestParseErrors:
def test_empty_string(self):
dt, err = parse_when("")
assert dt is None
assert "Empty" in err
def test_whitespace_only(self):
dt, err = parse_when(" ")
assert dt is None
assert "Empty" in err
def test_gibberish(self):
dt, err = parse_when("asdfghjkl")
assert dt is None
assert "Could not parse" in err
def test_past_iso(self):
dt, err = parse_when("2020-01-01T00:00:00")
assert err is None
assert dt == datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc)