JMAP invitation missing VTIMEZONE causes offset in Outlook

Issue Description

When an event is created via JMAP (e.g. through the Bulwark webmail client) and Stalwart sends out the resulting REQUEST (invitation) email, the attached event.ics contains a DTSTART;TZID=Europe/Berlin:... reference but no VTIMEZONE component defining that TZID.

When the same event is instead created via CalDAV (e.g. through Thunderbird), the event.ics that Stalwart sends out for the identical local time does include a full VTIMEZONE block for Europe/Berlin, including the currently valid DST rule.

Because the JMAP-triggered invitation is missing the VTIMEZONE definition, Outlook cannot correctly resolve what “Europe/Berlin” means for that DTSTART and displays the event one hour off (in our case, the calendar grid shows the event at 17:30 instead of the correct 16:30). Note that the email body text (rendered separately from the same event data) shows the correct time — only the .ics attachment is affected.

Expected Behavior

The event.ics attached to a JMAP-triggered REQUEST invitation should include a VTIMEZONE component for every TZID referenced in the event (same as the CalDAV-triggered path already does), so that clients like Outlook display the correct local time.

Actual Behavior

The VTIMEZONE component is missing entirely from the JMAP-triggered invitation’s event.ics. Outlook then displays the event time an offset.

Reproduction Steps

  1. Using a JMAP client (e.g. Bulwark webmail), create a calendar event for today at 16:30 Europe/Berlin, with TZID=Europe/Berlin and an external attendee.
  2. Send the invitation.
  3. Inspect the event.ics.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

PostgreSQL

Blob Storage

PostgreSQL

Search Engine

Internal

Directory Backend

Internal

Additional Context

Both event.ics files quoted below were generated by the same Stalwart server for events created at the exact same local time (2026-08-21 16:30 Europe/Berlin), the only difference is whether the event was created via JMAP or CalDAV.

event.ics generated via JMAP (Bulwark) — missing VTIMEZONE:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Stalwart Labs LLC//Stalwart Server//EN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART;TZID=Europe/Berlin:20260821T163000
END:VEVENT
END:VCALENDAR

event.ics generated via CalDAV (Thunderbird) — includes VTIMEZONE:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Stalwart Labs LLC//Stalwart Server//EN
METHOD:REQUEST
BEGIN:VTIMEZONE
TZID:Europe/Berlin
BEGIN:DAYLIGHT
TZOFFSETTO:+0200
TZOFFSETFROM:+0100
TZNAME:(DST)
DTSTART:19970330T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETTO:+0100
TZOFFSETFROM:+0200
TZNAME:(STD)
DTSTART:19971026T030000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=Europe/Berlin:20260821T163000
DTEND;TZID=Europe/Berlin:20260821T173000
END:VEVENT
END:VCALENDAR

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

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

Thanks again for another fast fix! Happy to confirm this is fixed in v0.16.19.