Migrating to v3
v3 has one breaking change, and it affects one specific shape of event. Most calendars upgrade without any changes.
What changed
Section titled “What changed”An event’s end is now exclusive: the event runs up to, but not including,
the instant you give it.
The practical effect is limited to one case — an end at exactly midnight:
// This is the shape that changes:{ start: '2025-01-13T00:00', end: '2025-01-14T00:00' }// v2: painted 13 and 14 January// v3: paints 13 January only
// This is unchanged — 23:59 falls during the 14th, so the 14th is covered:{ start: '2025-01-13T00:00', end: '2025-01-14T23:59' }This matches RFC 5545 §3.6.1,
which calls DTEND “the non-inclusive end of the event”, and the
Google Calendar API,
which documents end the same way. v2’s inclusive reading is why the same event
could be exported to another calendar and come back a day longer.
Does this affect you?
Section titled “Does this affect you?”| Your events | Affected |
|---|---|
| Timed events inside one day | No |
| All-day events created by the built-in form | No — they store 23:59, which still covers that day |
Any event whose end is a time during the day |
No |
Events you build yourself with an end at exactly midnight |
Yes — one day shorter |
Code reading CellInfo.end from onCellClick |
Yes — see below |
If nothing in your data ends at exactly midnight, there is nothing to do.
The details
Section titled “The details”-
Date grids read
endexclusively. An event ending at midnight stops as that day begins, exactly as one ending on the hour does not fill that hour. To keep a bar that reaches a given day, end it during that day (2025-01-14T23:59) or at the following midnight (2025-01-15T00:00). -
The form stores all-day ends differently, and it changes nothing you see. A new all-day event is stored as the midnight after the last covered day rather than
23:59, which is what the RFC requires and what other calendars expect on import. Both forms render the same days, so existing events keep displaying as they always did. The form still shows you the last covered day. This only matters if you readevent.endyourself or compare an iCal export: a one-day all-day event now exports asDTSTART;VALUE=DATE:20250804/DTEND;VALUE=DATE:20250805. -
CellInfo.endis the next midnight. WhatonCellClickreceives (and thedata-endattribute plugins read) now ends on the next boundary rather than at23:59, matching the hour and 15-minute cells, which already did. If you subtracted a second to get “the end of the day”, you can stop.
Checklist
Section titled “Checklist”- Search your event data for an
endat exactly midnight. Those bars are now one day shorter; end them during the intended last day instead. - Check any
onCellClickhandler that readsinfo.end. - If you compare against another calendar, re-export: the exported
DTENDis now the RFC’s exclusive value.