mangstadt / biweekly

biweekly is an iCalendar library written in Java.
BSD 2-Clause "Simplified" License
323 stars 44 forks source link

BYSETPOS skips first occurrence #122

Closed elmaimbo closed 1 year ago

elmaimbo commented 1 year ago

I've used https://icalendar.org/iCalendar-RFC-5545/3-8-5-3-recurrence-rule.html as the basis for some test cases, and I've discovered that the two BYSETPOS examples yield unexpected results. My observation is the recurrence dates returned miss the first occurrence. (FYI This sounds very similar to issue #89 ?)

E.g. Looking at this example (from URL above):

The third instance into the month of one of Tuesday, Wednesday, or Thursday, for the next 3 months:

DTSTART;TZID=America/New_York:19970904T090000 RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3

==> (1997 9:00 AM EDT) September 4;October 7 (1997 9:00 AM EST) November 6

The following program shows the issue:

import java.util.TimeZone;
import biweekly.Biweekly;
import biweekly.component.VEvent;
import biweekly.util.com.google.ical.compat.javautil.DateIterator;

public class ICalTest {

    public static void main(String[] args) {
        TimeZone tz = TimeZone.getDefault();

        System.out.println("JVM time zone=" + tz.getID());
        System.out.println();

        String iCalString =
                "BEGIN:VCALENDAR\r\n" +
                    "VERSION:2.0\r\n" +
                    "BEGIN:VEVENT\r\n" +
                        "DTSTART;TZID=America/New_York:19970904T090000\r\n" +
                        "RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3\r\n" +
                    "END:VEVENT\r\n" +
                "END:VCALENDAR\r\n";

        System.out.println(iCalString);

        VEvent vEvent = Biweekly.parse(iCalString).first().getEvents().get(0);
        DateIterator iterator = vEvent.getRecurrenceRule().getDateIterator(vEvent.getDateStart().getValue(),tz);
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

The output from the program (when including JVM argument -Duser.timezone="America/New_York" to mitigate any time-zone complications) is:

JVM time zone=America/New_York

BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
DTSTART;TZID=America/New_York:19970904T090000
RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3
END:VEVENT
END:VCALENDAR

Tue Oct 07 00:00:00 EDT 1997
Thu Nov 06 00:00:00 EST 1997
Thu Dec 04 00:00:00 EST 1997

However the expected output should be:

Thu Sep 04 00:00:00 EDT 1997
Tue Oct 07 00:00:00 EDT 1997
Thu Nov 06 00:00:00 EST 1997
mangstadt commented 1 year ago

Another problem: The time component of the returned dates does not match that of the DTSTART property. They are 00:00, but they should be 09:00.

Fixed in 477cfea95152536d66ec1418a26331e48f935c9d.

Biweekly now returns the same results as the two BYSETPOS examples on the iCalendar.org website.

elmaimbo commented 1 year ago

Thanks mangstadt. All my tests now pass on the latest build. :-)

mangstadt commented 1 year ago

Great, thanks for the update!