Liking cljdoc? Tell your friends :D

calendar

Handles recurring events, timezone-aware occurrence expansion, conflict detection, iCal export/import, and admin UI components for scheduling applications.

Key namespaces

NamespaceLayerResponsibility

wagoe.calendar.schema

shared

Malli schemas: EventData, EventDef, OccurrenceResult, ConflictResult

wagoe.calendar.ports

shared

CalendarAdapterProtocol, implemented by the :wagoe/calendar component

wagoe.calendar.core.event

core

duration, all-day?, within-range?

wagoe.calendar.shell.registry

shell

defevent macro and the event-type registry

wagoe.calendar.core.recurrence

core

RRULE parsing, occurrences, next-occurrence*, expand-event

wagoe.calendar.core.conflict

core

overlaps?, conflicts?, find-conflicts

wagoe.calendar.core.ui

core

Pure Hiccup: month-view, week-view, mini-calendar, event-badge

wagoe.calendar.core.feed

core

ical-response: Ring response for an iCal feed

wagoe.calendar.shell.adapters.ical

shell

ical4j adapter (ICalAdapter)

wagoe.calendar.shell.service

shell

Public API: export-ical, import-ical, ical-feed-response

Defining event types

(require '[wagoe.calendar.shell.registry :as registry])

(registry/defevent appointment-event
  {:id     :appointment
   :label  "Appointment"
   :schema [:map
            [:patient-id :uuid]
            [:room       :string]]})

(registry/get-event-type :appointment)   ;=> {:id :appointment ...}
(registry/list-event-types)              ;=> [:appointment ...]

Recurring events (RRULE)

(require '[wagoe.calendar.core.recurrence :as recurrence])

;; Expand weekly recurring event
(recurrence/occurrences event-data start-dt end-dt)

;; Next occurrence from an explicit reference instant
(recurrence/next-occurrence* event-data after-dt)

;; Full expansion with metadata
(recurrence/expand-event event-data range-start range-end)

Conflict detection

(require '[wagoe.calendar.core.conflict :as conflict])

(conflict/overlaps? event-a event-b)    ;=> true/false
(conflict/find-conflicts events window-start window-end)
;=> ({:event-a ... :event-b ... :overlap-start ... :overlap-end ...})

UI components

(require '[wagoe.calendar.core.ui :as ui])

(ui/month-view 2026 3 events {:timezone   "Europe/Amsterdam"
                              :event-href #(str "/appointments/" (:id %))
                              :t          t-fn})
(ui/week-view (java.time.LocalDate/of 2026 3 2) events {:t t-fn})
  • An event’s :href, or the :event-href option, turns its badge into a link.

  • Weekday and month labels are [:t …​] markers. They resolve only through wagoe.i18n.shell.render; plain hiccup2.core/html prints them as <t> tags. Pass :t (key → string) to resolve them when the hiccup is built.

  • Multi-day events show on every day they cover, clipped to the visible range; the week view puts those of 24 hours or more in an all-day row. Mark all-day events :all-day? true: they show no time and keep their dates in the event’s own :timezone.

iCal export / import

The :wagoe/calendar component implements CalendarAdapterProtocol. Take (ig/ref :wagoe/calendar) and call it through the ports:

(require '[wagoe.calendar.ports :as cal]
         '[wagoe.calendar.core.feed :as feed])

;; Export to iCal string
(cal/export-ical calendar events {})

;; Import from iCal string; :timezone places events without a TZID
(cal/import-ical calendar ical-string {:timezone "Europe/Amsterdam"})

;; Ring response for a feed (subscription URL): content type, filename, caching
(feed/ical-response (cal/export-ical calendar events {}) {:filename "appointments.ics"})

An :all-day? true event is written as DTSTART;VALUE=DATE with an exclusive DTEND, and a VALUE=DATE event is read back :all-day? true. event/all-day? reads that flag and nothing else.

Import reads a TZID that names an IANA zone, or a Windows one such as W. Europe Standard Time, with the JVM’s tz rules, whatever the feed’s VTIMEZONE says, and returns it as :timezone. Any other TZID is read with its VTIMEZONE, and :timezone is the option’s.

Without Integrant, wagoe.calendar.shell.service offers the same calls.

Dependencies

Uses org.mnode.ical4j/ical4j {:mvn/version "4.3.0"} (note: group ID is org.mnode.ical4j, Java packages are still net.fortuna.ical4j.*).

Testing

clojure -M:test :calendar

Can you improve this documentation? These fine people already did:
Thijs Creemers & thijscreemers
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close