Reading and writing the value types RFC 8984 defines.
JSCalendar is JSON, so every value arrives as a string, number, boolean, list or map. This module is the single place that decides what each becomes in Elixir and how it goes back — so a round trip is one pair of functions to reason about rather than a decision repeated at every property.
The mapping is deliberately to standard-library types, not to types invented here:
| JSCalendar | Elixir |
|---|---|
LocalDateTime | NaiveDateTime.t/0 |
UTCDateTime | DateTime.t/0 in Etc/UTC |
Duration, SignedDuration | Duration.t/0 |
String[Boolean] | MapSet.t/0 |
Id[Foo] | %{String.t() => Foo.t()} |
A String[Boolean] is a set written as an object whose values are
all true, so it reads back as a MapSet — "attendee" in participant.roles rather than a map lookup against a value that
carries no information.
Summary
Types
@type spec() :: :string | :boolean | :int | :unsigned_int | :local_date_time | :utc_date_time | :duration | :string_set | :string_map | :any | {:enum, [String.t()]} | {:list, spec()} | {:object, module()} | {:map_of, module()} | {:list_of, module()} | {:patch_map, spec()}
A property's declared JSCalendar type.
Functions
Read a JSON value as spec.
Arguments
valueis the decoded JSON value.specis aspec/0naming the JSCalendar type.
Returns
{:ok, value}with the Elixir representation; or{:error, reason}when the value does not match the type.
Examples
iex> JSCalendar.Type.decode("2026-06-02T09:00:00", :local_date_time)
{:ok, ~N[2026-06-02 09:00:00]}
iex> JSCalendar.Type.decode("PT1H", :duration)
{:ok, %Duration{hour: 1}}
iex> JSCalendar.Type.decode(%{"a" => true, "b" => true}, :string_set)
{:ok, MapSet.new(["a", "b"])}
iex> JSCalendar.Type.decode("nope", :local_date_time)
{:error, {:invalid_local_date_time, "nope"}}
Write an Elixir value back as JSON for spec.
nil is dropped by the caller rather than written as JSON null,
since RFC 8984 distinguishes an absent property from a null one.
Arguments
valueis the Elixir value.specis aspec/0naming the JSCalendar type.
Returns
- the JSON-ready value.
Examples
iex> JSCalendar.Type.encode(~N[2026-06-02 09:00:00], :local_date_time)
"2026-06-02T09:00:00"
iex> JSCalendar.Type.encode(MapSet.new(["a"]), :string_set)
%{"a" => true}