defmodule Draft.Type.Datetime do
use Timex
@moduledoc """
## example
iex> {:ok, _value} = Draft.Type.Datetime.cast("01-01-1900", [])
iex> {:ok, _value} = Draft.Type.Datetime.cast("1900-01-01", [])
iex> {:ok, _value} = Draft.Type.Datetime.cast("2016-02-29T22:25:00-06:00", [])
iex> {:ok, _value} = Draft.Type.Datetime.cast("2023-02-01T14:51:53.468132Z", [])
iex> {:error, _reason} = Draft.Type.Datetime.cast("1900-91-91", [])
"""
@iso_format ~r/^\d{4}-\d{2}-\d{2}T\d{1,2}:\d{1,2}:\d{1,2}((-\d{1,2}:\d{1,2})|(\.\d+Z))$/
@behaviour Draft.Type.Behaviour
@impl Draft.Type.Behaviour
def cast(nil, _opts) do
{:ok, nil}
end
@impl Draft.Type.Behaviour
def cast(value, _opts) when is_number(value) do
{:ok, Timex.from_unix(value)}
end
@impl Draft.Type.Behaviour
def cast(value, _opts) when is_binary(value) do
dvalue = String.trim(value)
cond do
String.match?(dvalue, ~r/^\d{4}-\d{2}-\d{2}$/) ->
Timex.parse(dvalue, "{YYYY}-{0M}-{D}")
String.match?(dvalue, ~r/^\d{2}-\d{2}-\d{4}$/) ->
Timex.parse(dvalue, "{D}-{0M}-{YYYY}")
String.match?(dvalue, @iso_format) ->
Timex.parse(dvalue, "{ISO:Extended}")
true ->
{:error, ["invalid datetime"]}
end
end
@impl Draft.Type.Behaviour
def cast(%Date{}=value, _opts) do
{:ok, value}
end
@impl Draft.Type.Behaviour
def cast(%DateTime{}=value, _opts) do
{:ok, value}
end
@impl Draft.Type.Behaviour
def cast(_value, _opts) do
{:error, ["invalid datetime"]}
end
@impl Draft.Type.Behaviour
def dump(value, _opts \\ []) do
{:ok, value}
end
end