Packages
zoi
0.11.1
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.1
0.9.0
0.9.0-rc.1
0.9.0-rc.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Zoi is a schema validation library for Elixir, designed to provide a simple and flexible way to define and validate data.
Current section
Files
Jump to
Current section
Files
lib/zoi/iso.ex
defmodule Zoi.ISO do
@moduledoc """
This module defines schemas for ISO time, date, and datetime formats,
along with transformations to convert them into Elixir's native types.
It includes built-in transformations to convert ISO time, date, and datetime
strings into `%Time{}`, `%Date{}`, and `%DateTime{}` structs.
`Zoi` main API have `Zoi.datetime/1`, `Zoi.date/1`, `Zoi.naive_datetime/1` and `Zoi.time/1` functions. These functions validates if the input
strings comply with the `Elixir` date formats. Use the `Zoi.ISO` module when you just want to validate ISO strings without the need to convert them to native types.
"""
@doc """
Defines a time type schema.
## Example
iex> schema = Zoi.ISO.time()
iex> Zoi.parse(schema, "12:34:56")
{:ok, "12:34:56"}
iex> Zoi.parse(schema, "25:00:00")
{:error,
[
%Zoi.Error{
code: :invalid_type,
message: "invalid type: expected ISO time",
issue: {"invalid type: expected ISO time", [type: :iso_time]},
path: []
}
]}
## Options
#{Zoi.Describe.generate(Zoi.ISO.Time.opts())}
"""
@doc group: "Basic Types"
def time(opts \\ []) do
Zoi.ISO.Time.opts()
|> Zoi.parse!(opts)
|> Zoi.ISO.Time.new()
end
@doc """
Defines a date type schema.
## Example
iex> schema = Zoi.ISO.date()
iex> Zoi.parse(schema, "2025-08-07")
{:ok, "2025-08-07"}
iex> Zoi.parse(schema, "2025-02-30")
{:error,
[
%Zoi.Error{
code: :invalid_type,
message: "invalid type: expected ISO date",
issue: {"invalid type: expected ISO date", [type: :iso_date]},
path: []
}
]}
## Options
#{Zoi.Describe.generate(Zoi.ISO.Date.opts())}
"""
@doc group: "Basic Types"
def date(opts \\ []) do
Zoi.ISO.Date.opts()
|> Zoi.parse!(opts)
|> Zoi.ISO.Date.new()
end
@doc """
Defines a datetime type schema.
## Example
iex> schema = Zoi.ISO.datetime()
iex> Zoi.parse(schema, "2025-08-07T10:04:22+03:00")
{:ok, "2025-08-07T10:04:22+03:00"}
iex> schema = Zoi.ISO.datetime()
iex> Zoi.parse(schema, 1754646043)
{:error,
[
%Zoi.Error{
code: :invalid_type,
message: "invalid type: expected ISO datetime",
issue: {"invalid type: expected ISO datetime", [type: :iso_datetime]},
path: []
}
]}
## Options
#{Zoi.Describe.generate(Zoi.ISO.DateTime.opts())}
"""
@doc group: "Basic Types"
def datetime(opts \\ []) do
Zoi.ISO.DateTime.opts()
|> Zoi.parse!(opts)
|> Zoi.ISO.DateTime.new()
end
@doc """
Defines a naive datetime type schema.
## Example
iex> schema = Zoi.ISO.naive_datetime()
iex> Zoi.parse(schema, "2025-08-07T10:04:22")
{:ok, "2025-08-07T10:04:22"}
iex> schema = Zoi.ISO.naive_datetime()
iex> Zoi.parse(schema, 1754646043)
{:error,
[
%Zoi.Error{
code: :invalid_type,
message: "invalid type: expected ISO naive datetime",
issue: {"invalid type: expected ISO naive datetime", [type: :iso_naive_datetime]},
path: []
}
]}
## Options
#{Zoi.Describe.generate(Zoi.ISO.NaiveDateTime.opts())}
"""
@doc group: "Basic Types"
def naive_datetime(opts \\ []) do
Zoi.ISO.NaiveDateTime.opts()
|> Zoi.parse!(opts)
|> Zoi.ISO.NaiveDateTime.new()
end
# Transforms
@doc """
Converts `Zoi.ISO.time()` to `%Time{}` struct.
## Example
iex> schema = Zoi.ISO.time() |> Zoi.ISO.to_time_struct()
iex> Zoi.parse(schema, "12:34:56")
{:ok, ~T[12:34:56]}
"""
@doc group: "Transforms"
def to_time_struct(schema) do
schema
|> Zoi.transform({__MODULE__, :__transform__, [[:to_time]]})
end
@doc """
Converts `Zoi.ISO.date()` to `%Date{}` struct.
## Example
iex> schema = Zoi.ISO.date() |> Zoi.ISO.to_date_struct()
iex> Zoi.parse(schema, "2025-08-07")
{:ok, ~D[2025-08-07]}
"""
@doc group: "Transforms"
def to_date_struct(schema) do
schema
|> Zoi.transform({__MODULE__, :__transform__, [[:to_date]]})
end
@doc """
Converts `Zoi.ISO.datetime()` to `%DateTime{}` struct.
## Example
iex> schema = Zoi.ISO.datetime() |> Zoi.ISO.to_datetime_struct()
iex> Zoi.parse(schema, "2025-08-07T10:04:22+03:00")
{:ok, ~U[2025-08-07 07:04:22Z]}
"""
@doc group: "Transforms"
def to_datetime_struct(schema) do
schema
|> Zoi.transform({__MODULE__, :__transform__, [[:to_datetime]]})
end
@doc """
Converts `Zoi.ISO.naive_datetime()` to `%NaiveDateTime{}` struct.
## Example
iex> schema = Zoi.ISO.naive_datetime() |> Zoi.ISO.to_naive_datetime_struct()
iex> Zoi.parse(schema, "2025-08-07T10:04:22")
{:ok, ~N[2025-08-07 10:04:22]}
"""
@doc group: "Transforms"
def to_naive_datetime_struct(schema) do
schema
|> Zoi.transform({__MODULE__, :__transform__, [[:to_naive_datetime]]})
end
# Transforms MFAs
@doc false
def __transform__(input, args, ctx: ctx) do
do_transform(ctx.schema, input, args)
end
def do_transform(%Zoi.ISO.Time{}, input, [:to_time]) do
# since `Zoi.ISO.Time` already validates the input as an ISO time,
# we can safely parse it to a Time struct
{:ok, parsed} = Time.from_iso8601(input)
{:ok, parsed}
end
def do_transform(%Zoi.ISO.Date{}, input, [:to_date]) do
# since `Zoi.ISO.Date` already validates the input as an ISO date,
# we can safely parse it to a Date struct
{:ok, parsed} = Date.from_iso8601(input)
{:ok, parsed}
end
def do_transform(%Zoi.ISO.DateTime{}, input, [:to_datetime]) do
# since `Zoi.ISO.DateTime` already validates the input as an ISO datetime,
# we can safely parse it to a DateTime struct
{:ok, parsed, _offset} = DateTime.from_iso8601(input)
{:ok, parsed}
end
def do_transform(%Zoi.ISO.NaiveDateTime{}, input, [:to_naive_datetime]) do
# since `Zoi.ISO.NaiveDateTime` already validates the input as an ISO naive datetime,
# we can safely parse it to a NaiveDateTime struct
{:ok, parsed} = NaiveDateTime.from_iso8601(input)
{:ok, parsed}
end
# coveralls-ignore-start
def do_transform(_schema, input, _args) do
# Default to the input if there is no type pattern match
{:ok, input}
end
# coveralls-ignore-stop
end