Skip to content

Instantly share code, notes, and snippets.

@James-E-A
Last active January 17, 2025 16:42
Show Gist options
  • Save James-E-A/539f74d3f9c8c0dea811c745c0aa44ce to your computer and use it in GitHub Desktop.
Save James-E-A/539f74d3f9c8c0dea811c745c0aa44ce to your computer and use it in GitHub Desktop.
Elixir parse HTML datetime form input values
defmodule FooWeb.Util do
# https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-local-date-and-time-string
@spec parse_web_time!(String.t()) :: NaiveDateTime.t()
@spec parse_web_time!(String.t(), Calendar.time_zone()) :: DateTime.t()
@spec parse_web_time!(String.t(), Calendar.time_zone(), Calendar.time_zone_database()) :: DateTime.t()
def parse_web_time!(<<s::binary-size(16)>>) do
Timex.parse!(s, "{YYYY}-{0M}-{0D}T{h24}:{m}")
end
def parse_web_time!(<<s::binary-size(19)>>) do
Timex.parse!(s, "{YYYY}-{0M}-{0D}T{h24}:{m}:{s}")
end
def parse_web_time!(<<s::binary>>) when byte_size(s) in 21..23 do
Timex.parse!(s, "{YYYY}-{0M}-{0D}T{h24}:{m}:{s}{ss}")
end
def parse_web_time!(_), do: (raise ArgumentError, "Expected 16, 19, or 21-23 character long ASCII string.")
def parse_web_time!(s, tz), do: parse_web_time!(s) |> DateTime.from_naive!(tz)
def parse_web_time!(s, tz, tz_db), do: parse_web_time!(s) |> DateTime.from_naive!(tz, tz_db)
@spec parse_web_time(String.t()) :: {:ok, NaiveDateTime.t()} | {:error, term()}
#spec parse_web_time(String.t(), "Etc/UTC") :: {:ok, DateTime.t()} | {:error, term()}
@spec parse_web_time(String.t(), Calendar.time_zone()) :: {:ok, DateTime.t()} | {:ambiguous, DateTime.t(), DateTime.t()} | {:gap, DateTime.t(), DateTime.t()} | {:error, term()}
@spec parse_web_time(String.t(), Calendar.time_zone(), Calendar.time_zone_database()) :: {:ok, DateTime.t()} | {:ambiguous, DateTime.t(), DateTime.t()} | {:gap, DateTime.t(), DateTime.t()} | {:error, term()}
def parse_web_time(<<s::binary-size(16)>>) do
Timex.parse(s, "{YYYY}-{0M}-{0D}T{h24}:{m}")
end
def parse_web_time(<<s::binary-size(19)>>) do
Timex.parse(s, "{YYYY}-{0M}-{0D}T{h24}:{m}:{s}")
end
def parse_web_time(<<s::binary>>) when byte_size(s) in 21..23 do
Timex.parse(s, "{YYYY}-{0M}-{0D}T{h24}:{m}:{s}{ss}")
end
def parse_web_time(_), do: {:error, :einval}
def parse_web_time(s, tz) do
case parse_web_time(s) do
{:ok, t} -> DateTime.from_naive(t, tz)
other -> other
end
end
def parse_web_time(s, tz, tz_db) do
case parse_web_time(s) do
{:ok, t} -> DateTime.from_naive(t, tz, tz_db)
other -> other
end
end
# https://github.com/erlang/otp/blob/OTP-26.2.5.6/lib/kernel/src/inet_parse.erl#L442
@spec parse_address(String.t()) :: {:ok, :inet.ip_address()} | {:error, :einval}
def parse_address(s) do
s |> String.to_charlist |> :inet_parse.address
end
@spec parse_address!(ip_address :: String.t()) :: :inet.ip_address()
def parse_address!(s) do
case parse_address(s) do
{:ok, ip_address} -> ip_address
{:error, :einval} -> raise ArgumentError, "invalid IP address"
end
end
@spec unparse_address(:inet.ip_address()) :: String.t()
def unparse_address(ip_address) do
# infallible for well-typed input
ip_address |> :inet_parse.ntoa |> List.to_string
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment