JSCalendar: A JSON Representation of Calendar Data
Extracted elements (30)
JSCalendar defines its data model independently from iCalendar rather than providing a direct JSON mapping like jCal. This avoids inheriting iCalendar's ambiguities around time zones, recurrence rule interpretation, and parser interoperability issues, which jCal inherited wholesale.
JSON was chosen as the representation format primarily as a pragmatic decision: widespread adoption makes JSCalendar easier to integrate, and production-ready JSON parsers eliminate parser-related interoperability issues that iCalendar has long suffered from. The stricter I-JSON subset is required for additional determinism.
PatchObject is used in recurrence overrides and localizations to dramatically reduce file size and duplicated content. Unlike iCalendar, which requires duplicating an entire event for a single override, JSCalendar records only the differing properties, allowing occurrences to automatically inherit changes (e.g., a location change) unless explicitly overridden.
For iCalendar compatibility, implementations MUST be able to receive and persist uid values of at least 255 octets. The prodId property SHOULD NOT be used to alter interpretation of a JSCalendar object beyond defined semantics, as this practice causes long-term interoperability problems.
A JSCalendar object MUST be valid I-JSON (a strict subset of JSON as defined in RFC 7493). Property names and values are case sensitive.
A PatchObject MUST be rejected in its entirety if any of its patches are invalid; implementations MUST NOT apply partial patches. Validity conditions include: no pointer into an array interior, all parent path segments must exist, no pointer may be a prefix of another pointer in the same PatchObject, and the value must be type-correct or null for an optional property.
Custom time zone identifiers in the timeZones map MUST start with '/' to avoid conflicts with IANA Time Zone Database names, and MUST be valid RFC 5545 paramtext values. At least one property in the JSCalendar object MUST reference each defined custom time zone (orphaned definitions are not allowed).
If recurrenceId is present, the recurrenceRules and recurrenceOverrides properties MUST NOT be present. The recurrenceIdTimeZone property MUST be set when recurrenceId is set and MUST NOT be set when recurrenceId is absent.
If the participants property is set and any participant has a sendTo property, the replyTo property of the calendar object MUST define at least one reply method.
The descriptionContentType MUST be a subtype of 'text' and SHOULD be 'text/plain' or 'text/html'. If a charset parameter is included, its value MUST be 'utf-8'.
The localizations PatchObject patches MUST only end with suffixes 'title', 'description', or 'name'. A patch MUST NOT have the prefix 'recurrenceOverrides'; localization of overrides must be a patch inside the override's own localizations property.
The privacy property (default 'public') MUST NOT affect information sent to scheduled participants; it is only interpreted by protocols sharing calendar objects between users. Unknown privacy values MUST be preserved and treated as equivalent to 'private'.
The sequence property (default 0) MUST be incremented by one every time a change is made to the object, except when the change only modifies the participants property. This supports iTIP (RFC 5546) version tracking.
The uid property is mandatory and MUST be globally unique; UUID version 4 (RFC 4122) is RECOMMENDED. Implementations MUST be able to receive and persist uid values of at least 255 octets and MUST NOT truncate values in the middle of a UTF-8 multi-octet sequence.
Vendor-specific property names MUST be prefixed by a domain name controlled by the vendor followed by a colon (e.g., 'example.com:customprop'). Vendor-specific enum values MUST follow the same domain-prefixed pattern. This prevents collision with IANA-registered names.
When converting a LocalDateTime that falls in a time zone discontinuity (e.g., a DST overlap where a wall-clock time occurs twice), the UTC offset before the transition MUST be used for conversion to UTC.
When splitting a recurring event for a 'this and future' change, the original object MUST be truncated to end before the split, a 'next' relation MUST be set on the original's relatedTo for the new object's UID, and a 'first' relation for the series' first UID MUST be set on the new object.
The privacy property allows owners to mark objects 'private' (only timing metadata shared) or 'secret' (hidden entirely when calendar is shared). When privacy is 'private', only a defined subset of properties (start, duration, uid, freeBusyStatus, etc.) MAY be shared; all others MUST NOT be.
Alert objects have a mandatory trigger of type OffsetTrigger (SignedDuration offset relative to event start or end), AbsoluteTrigger (specific UTCDateTime), or UnknownTrigger (for forward compatibility). Implementations SHOULD NOT trigger for unknown trigger types but MUST preserve them.
JSCalendar defines three top-level object types: Event (media type application/jscalendar+json;type=event), Task (application/jscalendar+json;type=task), and Group (application/jscalendar+json;type=group). Each MUST carry a matching @type property value ('Event', 'Task', or 'Group').
The Participant object represents a calendar participant with mandatory roles (String[Boolean] map) and optional fields including participationStatus (default 'needs-action'), scheduleAgent (default 'server'), expectReply, sendTo methods, and delegation tracking via delegatedTo/delegatedFrom/memberOf.
The recurrenceOverrides property maps LocalDateTime keys to PatchObjects. If a key does not match the recurrence rule output it is treated as an additional occurrence (RDATE equivalent). Setting 'excluded: true' in a patch omits the occurrence (EXDATE equivalent); such a patch MUST NOT modify any other property.
The RecurrenceRule object maps iCalendar RECUR with properties including frequency (mandatory), interval, rscale (calendar system, default 'gregorian'), skip (for invalid dates: 'omit'/'backward'/'forward'), firstDayOfWeek, and byX filter arrays (byDay, byMonthDay, byMonth, byYearDay, byWeekNo, byHour, byMinute, bySecond, bySetPosition), plus count or until for bounds.
RFC 8984 creates the 'JSCalendar Properties' registry (Section 8.2), the 'JSCalendar Types' registry (Section 8.3), and the 'JSCalendar Enum Values' registry (Section 8.4) under IANA. New entries require a Designated Expert review with a preliminary community review period.
Expanding recurrence rules can produce an unbounded number of occurrences. Implementations should limit expansion depth to prevent resource exhaustion attacks from maliciously crafted recurrence rules.
JSON parsing of untrusted calendar data may expose implementations to parser exploits. Since JSCalendar requires I-JSON (a strict subset), implementations should use a conformant parser and be wary of input crafted to exploit parser edge cases.
URI values embedded in JSCalendar objects (e.g., in Link.href, VirtualLocation.uri, Location.coordinates) may reference dangerous resources. Implementations should validate and sanitize URIs before use, particularly 'data:' URLs which can embed arbitrarily large content.
Duration uses a strict subset of ISO 8601 duration format defined by a specific ABNF grammar (P followed by optional week/day components and/or time components). Fractional seconds MUST NOT appear unless non-zero and MUST NOT have trailing zeros. Leap seconds MUST NOT be considered when adding or subtracting durations from LocalDateTime.
The Id type is a String of 1–255 octets and MUST only contain characters from the base64url alphabet (A-Za-z0-9, hyphen, underscore), excluding the pad character '='. It is used as map keys to name objects within a JSCalendar object.
UTCDateTime is an RFC 3339 date-time string where all letters MUST be uppercase and the time offset MUST be 'Z'. Fractional seconds MUST NOT be included unless non-zero, and MUST NOT have trailing zeros, ensuring a single canonical representation per date-time.