Skip to content

Use the revision of an override with the highest SEQUENCE - #115

Merged
OzzyCzech merged 2 commits into
mainfrom
fix/override-sequence
Oct 21, 2026
Merged

OzzyCzech merged 2 commits into
mainfrom
fix/override-sequence

Conversation

@OzzyCzech

@OzzyCzech OzzyCzech commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Several components with the same UID and RECURRENCE-ID are revisions of one instance. Both APIs returned all of them as occurrences, so a calendar with an updated override showed the instance twice: once at the old time, once at the new one.

Now only the revision with the highest SEQUENCE (RFC 5545, section 3.8.7.4) applies; a missing or invalid SEQUENCE is 0 and of equal revisions the later component wins (our tie-break, the RFC does not define one).

  • New API: Item::overrides() returns the current revisions in the order of the calendar. Instances are matched like the expansion matches them (as instants in any timezone, or as days for date-only IDs of a timed series), and RANGE is part of the identity, so a THISANDFUTURE override and a single override of the same instance both stay in effect. Revisions of overrides without their recurring item and of items without DTSTART are resolved too.
  • Array API: IcalParser::getEvents() skips superseded revisions and _RECURRENCE_IDS keeps the current one. The RECURRENCE-ID is compared as written together with its TZID and RANGE (the master's timezone is not known when an override is registered); $data['VEVENT'] itself still contains every component.

Not changed: overrides do not inherit missing properties from the master, revisions of the master itself (same UID without RECURRENCE-ID) are not resolved, and DTSTAMP is not used as a tie-break (RFC 5546, section 2.1.5).

Tests

  • Fixtures/Regression/duplicate-override-sequence.ics: a daily series with three duplicated overrides (higher SEQUENCE first, equal SEQUENCE, SEQUENCE vs none), checked with both APIs in regressions.phpt; fails without the fix.
  • regressions.phpt also covers a UTC and a TZID RECURRENCE-ID of the same instant, duplicated THISANDFUTURE overrides, a THISANDFUTURE and a single override of one instance, the same local time in two timezones (both APIs), orphan revisions (both APIs), a task without DTSTART, the order of overrides() and an invalid SEQUENCE (both APIs). The cases fixed by the review commit fail on the first commit; the two-timezone case of the new API is a guard (it passes on both).
  • composer check passes; no existing golden file or snapshot changed.

Several components with the same UID and RECURRENCE-ID are revisions of one
instance. All of them were returned as occurrences; now only the one with the
highest SEQUENCE applies, the later one in the calendar when they are equal
(RFC 5545, section 3.8.7.4).

- Item::overrides() returns the current revisions, matched as instants like
  the expansion (or as days for date-only IDs of a timed series), also for
  RANGE=THISANDFUTURE overrides
- IcalParser::getEvents() skips superseded revisions and _RECURRENCE_IDS keeps
  the current one; the RECURRENCE-ID is compared as written
- RANGE is part of the instance identity: a THISANDFUTURE override and a
  single override with the same RECURRENCE-ID are no longer revisions of
  each other (the range was dropped and later instances fell back)
- IcalParser keys revisions by RANGE, TZID and RECURRENCE-ID, so the same
  local time in another timezone no longer removes an override and brings
  back the original instance
- revisions of overrides without their recurring item and of items without
  DTSTART are resolved in the new API too, like in IcalParser
- overrides() keeps the order of the calendar
- IcalParser reads SEQUENCE like the new API (invalid values are 0)
- the RFC 5545 reference is attached to SEQUENCE, the tie-break is our own
@OzzyCzech
OzzyCzech merged commit d44e5e6 into main Oct 21, 2026
14 checks passed
@OzzyCzech
OzzyCzech deleted the fix/override-sequence branch October 21, 2026 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant