All-day events created via JMAP are served over CalDAV as floating DATE-TIME instead of VALUE=DATE

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

  1. Create an all-day event via JMAP CalendarEvent/set: “showWithoutTime”: true, “start”: “2026-08-02T00:00:00”, “duration”: “P1D”, “timeZone”: null.
  2. Fetch the event over CalDAV, or sync the calendar in Apple Calendar (iOS).
  3. DTSTART is a floating DATE-TIME; the event displays as 00:00–23:59 instead of all-day.
  4. 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

This isn’t covered in the current docs. A community member or staff member will follow up.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.17 (yet to be released).