kewisch / kewisch/ical.js

Recurrence expansion does not include DTSTART when not covered by the RRULE

Open
#294 4 comments 4 reactions 0 assignees View on GitHub
bug
Dominant language
JavaScript
Stars
1.2k
Forks
156
Avg merge
4d 43m
Merged PRs (30d)
3

Description

I ran into this issue because the calendar app on newer iPads, probably mistakingly, allows the creation of an event where the start date is not covered by the `RRULE`. It's possible to create a recurring event that eg. starts on a Wednesday, but has a weekly recurrence rule of only Thursdays.

Apple's iCalendar application and Google Calendar both display that oddball first instance, but my application (which uses ical.js) does not.

[The RFC5545 section about RRULE](https://tools.ietf.org/html/rfc5545#section-3.3.10) doesn't engrave in stone what's supposed to happen, but it does say (as part of the spec for `COUNT` in an `RRULE`):

> The "DTSTART" property value always counts as the first occurrence.

... So I'm fairly certain that this is a bug in ical.js.

Here's a reduced test case:

```js
const icalJs = require('ical.js');
const vevent = new icalJs.Event(new icalJs.Component(icalJs.parse(`
BEGIN:VCALENDAR
CALSCALE:GREGORIAN
METHOD:REQUEST
VERSION:2.0
BEGIN:VEVENT
CLASS:PUBLIC
CREATED:20160920T065356Z
DTSTART:20170301T100000
DTEND:20170301T110000
RRULE:FREQ=WEEKLY;BYDAY=TH
SUMMARY:Happens every Thursday, but starts on a Wednesday
UID:C2366E0E-09A4-4B9C-B9F5-EE96CEE467F0
END:VEVENT
END:VCALENDAR
`)).getFirstSubcomponent('vevent'));
const iterator = vevent.iterator();

let next;
let num = 0;
while ((next = iterator.next()) && num++ < 5) {
console.log(vevent.getOccurrenceDetails(next).startDate.toICALString());
}
```

Output:

```
20170302T100000
20170309T100000
20170316T100000
20170323T100000
20170330T100000
```

Expected output:

```
20170301T100000
20170302T100000
20170309T100000
20170316T100000
20170323T100000
```

Contributor guide

Open the contributing guide

Research direction

Reproduce the issue with the provided ical.js snippet, starting at vevent.iterator() and getOccurrenceDetails(). Trace how the recurrence iterator handles DTSTART when RRULE does not include it, then add a regression test that expects the DTSTART occurrence before the RRULE dates.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.