Reads JSCalendar (RFC 8984) documents into Tempo: each Event an
interval or, when it recurs, its occurrences, with the event's title,
status and the rest as metadata; a Group its member events; a Task
nothing, a task being work to do and not time taken.
The JSON is decoded by the optional
jscalendar dependency, and this
module is compiled only when it is present. An event's start is a
local time in its timeZone, floating when it has none. Its recurrence
rules are lowered to Tempo recurrences by Tempo.RRule, its excluded
rules take occurrences away, and its recurrence overrides replace or
remove the occurrence they name, or add one.
Not read: a rule's rscale other than Gregorian, a skip other than
omit, and a first day of the week other than Monday.
Summary
Functions
The events of a JSCalendar object already decoded, as parse/2 gives
them.
from_jscalendar/2, returning its answer or raising the error.
Reads a JSCalendar document into a set of intervals, one for each event or occurrence of an event, in order of start.
parse/2, returning its answer or raising the error.
Functions
@spec from_jscalendar(struct(), keyword()) :: {:ok, Tempo.t()} | {:error, Exception.t()}
The events of a JSCalendar object already decoded, as parse/2 gives
them.
Arguments
objectis aJSCalendar.Event,JSCalendar.TaskorJSCalendar.Group.optionsis a keyword list of options.
Options
:withinis as forparse/2.
Returns
{:ok, set}or{:error, exception}, as forparse/2.
Examples
iex> {:ok, object} =
...> JSCalendar.decode(
...> ~s({"@type":"Event","uid":"e","updated":"2026-06-01T09:00:00Z","start":"2026-06-02T09:00:00","duration":"PT1H"})
...> )
iex> {:ok, events} = Tempo.JSCalendar.from_jscalendar(object)
iex> Tempo.IntervalSet.map(events, &Tempo.to_iso8601!/1)
["2026Y6M2DT9H0M0S/T10H0M0S"]
from_jscalendar/2, returning its answer or raising the error.
Arguments
- The arguments are those of
from_jscalendar/2.
Options
- The options of
from_jscalendar/2.
Returns
- What
from_jscalendar/2returns as{:ok, answer}, the answer alone. The error it returns as{:error, exception}is raised.
Examples
iex> json = ~s({"@type":"Event","uid":"e","updated":"2026-06-01T09:00:00Z","start":"2026-06-02T09:00:00","duration":"PT1H"})
iex> {:ok, object} = JSCalendar.decode(json)
iex> object |> Tempo.JSCalendar.from_jscalendar!() |> Tempo.IntervalSet.count()
1
@spec parse(binary(), keyword()) :: {:ok, Tempo.t()} | {:error, Exception.t()}
Reads a JSCalendar document into a set of intervals, one for each event or occurrence of an event, in order of start.
Arguments
jsonis a JSCalendar document as text.optionsis a keyword list of options.
Options
:withinis aTempo.t/0, the window whose events are wanted: every occurrence that overlaps it is kept. It is required when an event recurs with neithercountnoruntil.
Returns
{:ok, set}, aTempo.t/0of the:allform, each member's metadata its event's (:uid,:title,:description,:status,:free_busy_status,:privacy,:time_zone,:sequence).{:error, %ArgumentError{}}when the document cannot be decoded, naming the reason thejscalendarlibrary gives, and{:error, exception}when a zone is unknown, a rule is not supported or a recurrence has no end and no window is given.
Examples
iex> json = ~s({
...> "@type": "Event",
...> "uid": "review",
...> "updated": "2026-06-01T09:00:00Z",
...> "title": "Quarterly review",
...> "start": "2026-06-02T09:00:00",
...> "duration": "PT1H"
...> })
iex> {:ok, events} = Tempo.JSCalendar.parse(json)
iex> [review] = Tempo.IntervalSet.members(events)
iex> {Tempo.to_iso8601!(review), Tempo.metadata(review).title}
{"2026Y6M2DT9H0M0S/T10H0M0S", "Quarterly review"}
parse/2, returning its answer or raising the error.
Arguments
- The arguments are those of
parse/2.
Options
- The options of
parse/2.
Returns
- What
parse/2returns as{:ok, answer}, the answer alone. The error it returns as{:error, exception}is raised.
Examples
iex> json = ~s({"@type":"Event","uid":"e","updated":"2026-06-01T09:00:00Z","start":"2026-06-02T09:00:00","duration":"PT1H"})
iex> json |> Tempo.JSCalendar.parse!() |> Tempo.IntervalSet.count()
1