Hi,
the Apple Calendar client sent the following iCal event:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Apple Inc.//Mac OS X 10.10.3//EN
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:Asia/Shanghai
BEGIN:STANDARD
TZOFFSETFROM:+0900
RRULE:FREQ=YEARLY;UNTIL=19910914T150000Z;BYMONTH=9;BYDAY=3SU
DTSTART:19890917T000000
TZNAME:GMT+8
TZOFFSETTO:+0800
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0800
DTSTART:19910414T000000
TZNAME:GMT+8
TZOFFSETTO:+0900
RDATE:19910414T000000
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
CREATED:20150629T100123Z
UID:835A47A1-464E-440E-9DA7-027507822ED7
DTSTART;TZID=Asia/Shanghai:20150630T190000
DTEND;TZID=Asia/Shanghai:20150630T200000
TRANSP:OPAQUE
SUMMARY:test
DTSTAMP:20150629T100123Z
SEQUENCE:0
END:VEVENT
END:VCALENDAR
The user obviously intended to create an appointment from 19:00 - 20:00 in his local timezone (Asia/Shanghai), the unix timestamp of the start date would be 1435662000000.
However, after parsing with iCal4j, the start- and endtimes are shifted by one hour (i.e. 18:00 - 19:00). Considering only the VTIMEZONE component of the iCal file, the DAYLIGHT observance seems to be (always) effective, with TZOFFSETTO set to "+0900", consequently it gets parsed with the observed offset (unix timestamp of the start time 1435658400000).
I'm not entirely sure if there is a bug in the VTIMEZONE declaration of the Mac OS client, or if iCal4j is misinterpreting it? I currently have the feeling that it boils down to the last onset of the recurrence rule in the STANDARD observance, i.e. if it is considered to happen before, or after the RDATE of the DAYLIGHT observance: The 3rd sunday in september 1991 was the 15th, however, the UNTIL date is specified as 19910914T150000Z, and I wonder if an offset of +8 or +9 hours should be applied to this UTC date?
Playing around with different UNTIL values in the RRULE, it seems that it behaves "correct" for values >= "19910915T000000Z".
In
Observance.getLatestOnset(Date), for the initial onset, the TZFROM offset is not added as it would potentially lead to wrong BYDAY comparisons (as described in the source code comments). This leads to all "candidates", which are calculated based on the initial onset inRecur.getCandidates(Date, Value), also not having the TZFROM offset applied.But then, when checking if the candidates are within the recurrence rule range in
Recur.getDates(Date, Date, Date, Value, int), the last candidate (3rd sunday in september 1991) happens to be after the parsed until value, as it was originally parsed to 1991-09-14 15:00:00 UTC or 1991-09-15 00:00:00 Asia/Shanghai, while the candidate was calculated to 1991-09-15 00:00:00 UTC.