Issue Description
All-day events created via JMAP CalendarEvent/set (JSCalendar with showWithoutTime: true, timeZone: null, duration: P1D) are stored and served over CalDAV with a floating DATE-TIME DTSTART plus the draft property SHOW-WITHOUT-TIME:TRUE, instead of DTSTART;VALUE=DATE. CalDAV clients that don’t implement the JSCalendar extensions draft (e.g. Apple Calendar) render them as timed events from 00:00 to 23:59.
The cause appears to be in calcard’s JSCalendar→iCalendar export: showWithoutTime is never mapped to a VALUE=DATE value type (the DTSTART emission in src/jscalendar/export/convert.rs / insert_date in export/params.rs always writes a full date-time). VALUE=DATE is only ever restored from convertedProperties round-trip metadata, which a natively JMAP-created event doesn’t have. This behavior is pinned by the fixture resources/jscalendar/010_complete.txt (“All-Day Event”). Events created via CalDAV PUT round-trip correctly because the original ICS value types are preserved.
Expected Behavior
An all-day JSCalendar event should be exported with RFC 5545 all-day semantics: DTSTART;VALUE=DATE:20260802 plus a DATE-typed DTEND (or day-granular DURATION), so any CalDAV client displays it as all-day.
Actual Behavior
CalDAV GET returns:
BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTART:20260802T000000
UID:d483e96d-ea11-43e3-bc68-a4dda82bfaab
SUMMARY:Test 1
DURATION:P1D
STATUS:CONFIRMED
TRANSP:OPAQUE
CLASS:PUBLIC
SHOW-WITHOUT-TIME:TRUE
END:VEVENT
END:VCALENDAR
Apple Calendar shows this as a timed event 00:00–23:59. (Side observation: the generated VCALENDAR also lacks the required VERSION/PRODID properties, and the VEVENT lacks DTSTAMP.)
Reproduction Steps
- Create an all-day event via JMAP CalendarEvent/set: “showWithoutTime”: true, “start”: “2026-08-02T00:00:00”, “duration”: “P1D”, “timeZone”: null.
- Fetch the event over CalDAV, or sync the calendar in Apple Calendar (iOS).
- DTSTART is a floating DATE-TIME; the event displays as 00:00–23:59 instead of all-day.
- For contrast, create an all-day event from iOS (CalDAV PUT with DTSTART;VALUE=DATE) — it round-trips correctly.
Stalwart Version
v0.16.x
Installation Method
Built from source
Database Backend
FoundationDB
Blob Storage
S3-compatible
Search Engine
Elasticsearch
Directory Backend
Internal
Additional Context
- Client: Apple Calendar, iOS 27.0 (CalDAV). Any client without JSCalendar-extensions support is affected.
- Suggested fix: when showWithoutTime == true and the start has zero time / whole-day duration, emit DATE value types; the existing guard in export/convert.rs (which skips SHOW-WITHOUT-TIME when DATE-typed entries are present) then composes correctly. Fixture 010_complete.txt would need updating.
I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.
on
I have searched this support forum (open and closed topics) and confirm this is not a duplicate.
on
I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.
on