philomena/lib/relative_date/parser.ex
2019-11-18 22:38:22 -05:00

87 lines
1.9 KiB
Elixir

defmodule RelativeDate.Parser do
import NimbleParsec
time_specifier =
choice([
string("second") |> replace(1),
string("minute") |> replace(60),
string("hour") |> replace(3_600),
string("day") |> replace(86_400),
string("week") |> replace(604_800),
string("month") |> replace(2_592_000),
string("year") |> replace(31_536_000)
])
|> ignore(optional(string("s")))
direction_specifier =
choice([
string("ago") |> replace(-1),
string("from now") |> replace(1)
])
space = ignore(repeat(string(" ")))
moon =
space
|> string("moon")
|> concat(space)
|> eos()
|> unwrap_and_tag(:moon)
date =
space
|> integer(min: 1)
|> concat(space)
|> concat(time_specifier)
|> concat(space)
|> concat(direction_specifier)
|> concat(space)
|> eos()
|> tag(:relative_date)
relative_date =
choice([
moon,
date
])
defparsecp :relative_date, relative_date
def parse(input) do
input =
input
|> to_string()
|> String.trim()
case parse_absolute(input) do
{:ok, datetime} ->
{:ok, datetime}
_error ->
parse_relative(input)
end
end
def parse_absolute(input) do
case DateTime.from_iso8601(input) do
{:ok, datetime, _offset} ->
{:ok, datetime |> DateTime.truncate(:second)}
_error ->
{:error, "Parse error"}
end
end
def parse_relative(input) do
case relative_date(input) do
{:ok, [moon: _moon], _1, _2, _3, _4} ->
{:ok, DateTime.utc_now() |> DateTime.add(31_536_000_000, :second) |> DateTime.truncate(:second)}
{:ok, [relative_date: [amount, scale, direction]], _1, _2, _3, _4} ->
{:ok, DateTime.utc_now() |> DateTime.add(amount * scale * direction, :second) |> DateTime.truncate(:second)}
_error ->
{:error, "Parse error"}
end
end
end