Parses duration expressions into {unit, n} tuples suitable for
DateTime.shift/2.
Supported formats:
"in X units"— future (e.g."in 2 seconds")"X units from now"— future"X units ago"— past"X units"— future (bare form)"next monday"/"last friday"— resolved to a{:day, n}shift"this monday"/"on friday"— upcoming including today"next week"/"last month"/"next year"etc.
Units accept singular or plural forms exactly (second/seconds, etc.).
Extra units: fortnight (= 14 days), quarter (= 3 months),
decade (= 10 years), century (= 100 years).
Numbers may include commas ("1,000 seconds") and may be fractional
("in 1.5 hours"). Fractional durations are internally converted to a
{:microsecond, n} tuple; fractional months and years are rejected
because they have no unambiguous conversion.
Weekday expressions are resolved against :reference_date, which
defaults to DateTime.utc_now/0.
Examples
iex> Chronix.Duration.parse("in 2 seconds")
{:ok, {:second, 2}}
iex> Chronix.Duration.parse("2 seconds ago")
{:ok, {:second, -2}}
iex> Chronix.Duration.parse("5 months from now")
{:ok, {:month, 5}}
iex> Chronix.Duration.parse("next monday", reference_date: ~U[2025-01-27 00:00:00Z])
{:ok, {:day, 7}}
iex> Chronix.Duration.parse("in 2 seconds ago")
{:error, "cannot combine 'in' and 'ago'"}