Add the iCalendar round-trip
Parsing goes through icalendar's low-level parser, which keeps properties in order and keeps repeated ones. Writing is ours: that crate's writer escapes a whole property value as text, so CATEGORIES:Work,Personal would go out as one category named "Work,Personal" to every other client. Anything the model does not interpret is carried rather than dropped -- X-MOZ-LASTACK, X-EVOLUTION-ALARM-UID, ACKNOWLEDGED, the X-MICROSOFT-CDO set, unrecognised ATTENDEE parameters, and whole VTODO/VJOURNAL components. A calendar has several clients writing to it and this one is not the authority on which properties matter. VTIMEZONE is modelled properly, and TZID is stored exactly as written: Exchange names its zones "Pacific Standard Time", which no IANA lookup resolves, and normalising at parse time would make the document unrepresentable. Mapping to a real zone belongs at the point of use. Tested against a golden corpus captured from the live Baikal (seven producing clients over five years) and a published Outlook feed, scrubbed of private content with the structure left byte-for-byte. Eight hand-written fixtures cover what neither server had: DURATION, floating times, RDATE, DST boundaries, leap day, and the full escape set. The contract is that parse -> write -> parse is stable, plus a check that no property name loses occurrences across the trip, since a parser that dropped ATTENDEE entirely would round-trip perfectly and still be wrong.
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
BEGIN:VCALENDAR
|
||||
METHOD:PUBLISH
|
||||
PRODID:Microsoft Exchange Server 2010
|
||||
VERSION:2.0
|
||||
X-WR-CALNAME:Calendar
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Central Standard Time
|
||||
BEGIN:STANDARD
|
||||
DTSTART:16010101T020000
|
||||
TZOFFSETFROM:-0500
|
||||
TZOFFSETTO:-0600
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
DTSTART:16010101T020000
|
||||
TZOFFSETFROM:-0600
|
||||
TZOFFSETTO:-0500
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Mountain Standard Time
|
||||
BEGIN:STANDARD
|
||||
DTSTART:16010101T020000
|
||||
TZOFFSETFROM:-0600
|
||||
TZOFFSETTO:-0700
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
DTSTART:16010101T020000
|
||||
TZOFFSETFROM:-0700
|
||||
TZOFFSETTO:-0600
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:Pacific Standard Time
|
||||
BEGIN:STANDARD
|
||||
DTSTART:16010101T020000
|
||||
TZOFFSETFROM:-0700
|
||||
TZOFFSETTO:-0800
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
DTSTART:16010101T020000
|
||||
TZOFFSETFROM:-0800
|
||||
TZOFFSETTO:-0700
|
||||
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:\nJoin: https://example.org/meet/d5b85f228adc\n
|
||||
RRULE:FREQ=WEEKLY;UNTIL=20270825T190000Z;INTERVAL=1;BYDAY=WE;WKST=SU
|
||||
EXDATE;TZID=Pacific Standard Time:20251217T120000,20251224T120000,20251231T
|
||||
120000,20260513T120000,20260701T120000,20260805T120000,20260819T120000,202
|
||||
60826T120000,20260902T120000,20260909T120000
|
||||
UID:5d2296acb04b3a6d80097d073f@google.com
|
||||
SUMMARY:Handoff Triage
|
||||
DTSTART;TZID=Pacific Standard Time:20251015T120000
|
||||
DTEND;TZID=Pacific Standard Time:20251015T122500
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:29
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:29
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:1
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:\nJoin: https://example.org/meet/d5b85f228adc\n
|
||||
UID:5d2296acb04b3a6d80097d073f@google.com
|
||||
RECURRENCE-ID;TZID=Pacific Standard Time:20251015T120000
|
||||
SUMMARY:Handoff Triage
|
||||
DTSTART;TZID=Pacific Standard Time:20251016T110000
|
||||
DTEND;TZID=Pacific Standard Time:20251016T113000
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:29
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:29
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:3
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:\nJoin: https://example.org/meet/d5b85f228adc\n
|
||||
UID:5d2296acb04b3a6d80097d073f@google.com
|
||||
RECURRENCE-ID;TZID=Pacific Standard Time:20251105T120000
|
||||
SUMMARY:Handoff Triage
|
||||
DTSTART;TZID=Pacific Standard Time:20251105T120000
|
||||
DTEND;TZID=Pacific Standard Time:20251105T122500
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:29
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:29
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:3
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:\nJoin: https://example.org/meet/d5b85f228adc\n
|
||||
UID:5d2296acb04b3a6d80097d073f@google.com
|
||||
RECURRENCE-ID;TZID=Pacific Standard Time:20251112T120000
|
||||
SUMMARY:Handoff Triage
|
||||
DTSTART;TZID=Pacific Standard Time:20251112T120000
|
||||
DTEND;TZID=Pacific Standard Time:20251112T122500
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:29
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:29
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:3
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:\nJoin: https://example.org/meet/d5b85f228adc\n
|
||||
UID:5d2296acb04b3a6d80097d073f@google.com
|
||||
RECURRENCE-ID;TZID=Pacific Standard Time:20251210T120000
|
||||
SUMMARY:Handoff Triage
|
||||
DTSTART;TZID=Pacific Standard Time:20251209T130000
|
||||
DTEND;TZID=Pacific Standard Time:20251209T133000
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:29
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:29
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:3
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:Briefing\nJoin: https://example.org/meet/5934b829a167\n
|
||||
UID:D5BFA26242816AE1AB2B2D7D14A1B96E3C984A142C64CCBBBF1CC42774AC46FAD5BFA26
|
||||
242816AE1AB2B2D7D14A1B96E3C984A142C64CCBB
|
||||
RECURRENCE-ID;TZID=Mountain Standard Time:20260526T133000
|
||||
SUMMARY:Briefing Onboarding
|
||||
DTSTART;TZID=Mountain Standard Time:20260526T133000
|
||||
DTEND;TZID=Mountain Standard Time:20260526T140000
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:1
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:1
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:3
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DESCRIPTION:\nJoin: https://example.org/meet/5cfc4622592a\n
|
||||
UID:16A4709A509B3E413F90C38AB729CEA79577D656B130C5C3251996E325F3B9AF16A4709
|
||||
A509B3E413F90C38AB729CEA79577D656B130C5C3
|
||||
SUMMARY:Planning Check-in
|
||||
DTSTART;TZID=Central Standard Time:20260406T093000
|
||||
DTEND;TZID=Central Standard Time:20260406T101500
|
||||
CLASS:PUBLIC
|
||||
PRIORITY:5
|
||||
DTSTAMP:20260826T175427Z
|
||||
TRANSP:OPAQUE
|
||||
STATUS:CONFIRMED
|
||||
SEQUENCE:1
|
||||
LOCATION:Annex, Room 7
|
||||
X-MICROSOFT-CDO-APPT-SEQUENCE:1
|
||||
X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE
|
||||
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
|
||||
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
|
||||
X-MICROSOFT-CDO-IMPORTANCE:1
|
||||
X-MICROSOFT-CDO-INSTTYPE:0
|
||||
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
|
||||
X-MICROSOFT-DISALLOW-COUNTER:FALSE
|
||||
X-MICROSOFT-REQUESTEDATTENDANCEMODE:DEFAULT
|
||||
X-MICROSOFT-ISRESPONSEREQUESTED:FALSE
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
Reference in New Issue
Block a user