Current section

Files

Jump to
expression lib date_helpers.ex
Raw

lib/date_helpers.ex

defmodule Expression.DateHelpers do
@moduledoc false
import NimbleParsec
def date_separator(combinator \\ empty()) do
combinator
|> choice([
string("-"),
string("/")
])
end
def us_date(combinator \\ empty()) do
combinator
|> integer(2)
|> ignore(date_separator())
|> integer(2)
|> ignore(date_separator())
|> integer(4)
end
def plain_time(combinator \\ empty()) do
combinator
|> integer(2)
|> ignore(string(":"))
|> integer(2)
|> optional(ignore(string(":")))
|> optional(integer(2))
end
def us_datetime(combinator \\ empty()) do
combinator
|> us_date()
|> ignore(string(" "))
|> concat(plain_time())
end
def iso_date(combinator \\ empty()) do
combinator
|> integer(4)
|> ignore(date_separator())
|> integer(2)
|> ignore(date_separator())
|> integer(2)
end
def iso_time(combinator \\ empty()) do
combinator
|> integer(2)
|> ignore(string(":"))
|> integer(2)
|> ignore(string(":"))
|> integer(2)
|> ignore(optional(string(".")))
|> optional(integer(min: 1))
|> choice([
ignore(string("+")) |> integer(min: 1),
string("Z") |> replace(0),
empty()
])
end
def iso_datetime(combinator \\ empty()) do
combinator
|> iso_date()
|> ignore(string("T"))
|> concat(iso_time())
end
def time(combinator \\ empty()) do
combinator
|> plain_time()
|> reduce(:to_time)
end
def date(combinator \\ empty()) do
combinator
|> choice([
tag(us_date(), :us_format),
tag(iso_date(), :iso_format)
])
|> reduce(:to_date)
end
def datetime(combinator \\ empty()) do
combinator
|> choice([
tag(us_datetime(), :us_format),
tag(iso_datetime(), :iso_format)
])
|> reduce(:to_datetime)
end
def to_time(parsed_values) do
values =
[:hour, :minute, :second]
|> Enum.zip(parsed_values)
Time.new!(values[:hour], values[:minute], values[:second] || 0)
end
def to_datetime(opts) do
values =
case opts do
[iso_format: parsed_value] ->
values =
[:year, :month, :day, :hour, :minute, :second, :microsecond, :utc_offset]
|> Enum.zip(parsed_value)
{microseconds, values} = Keyword.pop(values, :microsecond, 0)
microsecond_entry =
{microseconds,
microseconds
|> to_string()
|> String.length()}
Keyword.put(values, :microsecond, microsecond_entry)
[us_format: parsed_value] ->
[:day, :month, :year, :hour, :minute, :second]
|> Enum.zip(parsed_value)
end
fields =
[
calendar: Calendar.ISO,
hour: 0,
minute: 0,
second: 0,
time_zone: "Etc/UTC",
zone_abbr: "UTC",
utc_offset: 0,
std_offset: 0
]
|> Keyword.merge(values)
struct(DateTime, fields)
end
def to_date(opts) do
values =
case opts do
[iso_format: parsed_value] ->
Enum.zip([:year, :month, :day], parsed_value)
[us_format: parsed_value] ->
Enum.zip([:day, :month, :year], parsed_value)
end
Date.new!(values[:year], values[:month], values[:day])
end
@spec extract_dateish(DateTime.t() | Date.t() | String.t() | nil) :: Date.t() | nil
def extract_dateish(date_time) when is_struct(date_time, DateTime), do: date_time
def extract_dateish(date) when is_struct(date, Date), do: date
def extract_dateish(nil), do: nil
def extract_dateish(expression) when is_binary(expression) do
expression = Regex.replace(~r/[a-z]/u, expression, "")
case Expression.parse_expression(expression) do
{:ok, [{:literal, datetime}]} when is_struct(datetime, DateTime) ->
DateTime.to_date(datetime)
{:ok, [{:literal, date}]} when is_struct(date, Date) ->
date
_other ->
nil
end
end
@spec extract_datetimeish(DateTime.t() | Date.t() | String.t() | nil) :: DateTime.t() | nil
def extract_datetimeish(nil), do: nil
def extract_datetimeish(date_time) when is_struct(date_time, DateTime), do: date_time
def extract_datetimeish(date) when is_struct(date, Date),
do: DateTime.new!(date, Time.new!(0, 0, 0, 0))
def extract_datetimeish(expression) when is_binary(expression) do
expression = Regex.replace(~r/[a-z]/u, expression, "")
case Expression.parse_expression(expression) do
{:ok, [{:literal, datetime}]} when is_struct(datetime, DateTime) ->
datetime
{:ok, [{:literal, date}]} when is_struct(date, Date) ->
DateTime.new!(date, ~T[00:00:00])
_other ->
nil
end
end
@spec extract_timeish(DateTime.t() | Time.t() | String.t()) :: Time.t() | nil
def extract_timeish(datetime) when is_struct(datetime, DateTime),
do: DateTime.to_time(datetime)
def extract_timeish(time) when is_struct(time, Time),
do: time
def extract_timeish(expression) when is_binary(expression) do
expression = Regex.replace(~r/[a-z\s]/u, expression, "")
case Expression.parse_expression(expression) do
{:ok, [{:literal, datetime}]} when is_struct(datetime, DateTime) ->
DateTime.to_time(datetime)
{:ok, [{:literal, time}]} when is_struct(time, Time) ->
time
{:ok, result} ->
result
_other ->
nil
end
end
end