The built-in formats, and nothing else.
A format is a fixed pair Rupa owns: a decoder from the wire string, and the encoder that inverts it. There is no way to add one, which is what keeps a schema pure data and keeps every format expressible as JSON Schema.
Each one means what JSON Schema's format of the same name means, and the official format
corpus is the test of that: test/rupa/format_suite_test.exs runs every string case in it
and allows no disagreement. In practice:
:date_time,:dateand:timeare RFC 3339'sdate-time,full-dateandfull-time: four-digit years, two-digit fields,.for a fraction,TandZin either case, and an offset on every time. A time decodes to UTC, as a date-time does, and a leap second (23:59:60, in UTC) decodes to the second before it, becauseCalendarhas no second 60.:durationis RFC 3339 appendix A's grammar: whole numbers, weeks only on their own, and a year, month and day (or hour, minute and second) nested so that none is skipped between two that are written.P1Y2Dis not a duration;P1Y0M2Dis.:emailis RFC 5321'sMailbox: a dot-atom or a quoted string, then@, then a hostname or an address literal ([127.0.0.1],[IPv6:::1]). ASCII only.:hostnameis RFC 1123's, and anxn--label in it must be an IDNA2008 A-label: Punycode for a Unicode label in NFC, every code point of which RFC 5892 allows, its contextual rules included. The Bidi rule (RFC 5893) is the one check not applied.:urineeds a scheme and well-formed percent-encoding;:ipv4and:ipv6are the dotted quad and RFC 4291's text form, with no zone id.
Four of them change the value: :date_time, :date, :time and :duration decode to
DateTime, Date, Time and Duration. The other six validate and hand the string back.
encode/2 is the inverse, and inverse is meant literally: whatever encode/2 produces,
decode/2 accepts and returns what you started with. That is why it refuses what the wire
cannot say rather than writing something close: a negative duration, a fraction of a second in
one, a week beside another unit, a year outside 0000–9999. A DateTime in another zone
is written in UTC, which is where decoding it will put it.
Summary
Functions
Decodes a string in the named format.
Encodes a decoded value back to its wire string.
Every format, in the order the documentation lists them.
Functions
Decodes a string in the named format.
Returns :error rather than a reason: the caller knows the format and the path, which is
the whole message.
iex> Rupa.Format.decode(:date, "2026-09-16")
{:ok, ~D[2026-09-16]}
iex> Rupa.Format.decode(:time, "08:30:00+07:00")
{:ok, ~T[01:30:00]}
iex> Rupa.Format.decode(:uuid, "not-a-uuid")
:error
Encodes a decoded value back to its wire string.
Returns :error for a value the format cannot represent — including the wrong type, since
encoding is where a DateTime field finds out it was handed a string.
iex> Rupa.Format.encode(:date, ~D[2026-09-16])
{:ok, "2026-09-16"}
iex> Rupa.Format.encode(:time, ~T[01:30:00])
{:ok, "01:30:00Z"}
iex> Rupa.Format.encode(:date, "2026-09-16")
:error
@spec names() :: [atom()]
Every format, in the order the documentation lists them.
iex> length(Rupa.Format.names())
10