Question

I'm trying to make an iCal feed (RFC 2445) for work holidays that won't need yearly updates, by listing holidays per their definition, rather than which dates they occur on on particular years.

Holidays like Memorial day (last Monday in May) of course don't need any special treatment beyond

BEGIN:VEVENT
DTSTAMP:20130210T211949Z
UID:20130210-memorial-day@usa.gov
DTSTART;VALUE=DATE:20130527
DTEND;VALUE=DATE:20130527
SUMMARY:Memorial Day
RRULE:FREQ=YEARLY;BYMONTH=5;BYDAY=-1MO;WKST=SU
END:VEVENT

...but when it comes to date-based holidays like New Year's Day, how does one encode the day slip on weekend Jan 1sts?

Was it helpful?

Solution

you should be aware that even though google calendar follows RFC2445, it has been obsoleted by RFC5545 which has made the EXRULE obsolete (deprecated features from RFC2445).

in the case of UK new year, it is:

BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
PRODID:pyICSParser
BEGIN:VEVENT
DTSTART;VALUE=DATE:20070101
RRULE:FREQ=YEARLY;BYMONTH=1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=1,2,3;BYSETPOS=1
UID:UIDnewyear_SO14805248@stackeroverflow.com
DTSTAMP:19970714T170000Z
SUMMARY: new year
END:VEVENT
END:VCALENDAR

which works like a charm under google calendar, outlook.com(/hotmail/...), yahoo calendars and on iOS devices synching from a Google Calendar.

UPDATE: for the USA new year

it would be a combination of 2 RRULE (RFC says SHOULD NOT occur more than once which seems to leave the possibility of 2 RRULE by event:

RRULE:FREQ=YEARLY;BYMONTH=1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=1,2;BYSETPOS=1
RRULE:FREQ=YEARLY;BYMONTH=12;BYDAY=FR;BYMONTHDAY=-1

Google Calendar supports it but looks like yahoo and hotmail/outlook.com don't, so 2 events would need to be created (one with each RRULE). If needed the use of RELATED-TO property could help keep track of their relations.

OTHER TIPS

NOTE: The following answer demonstrates how to do it with a deprecated RFC! I am leaving it for reference, in case anyone ever needs to. See the proper answer for more useful facts!

You can do this by adding corresponding yearly repeating adjacent-Friday and adjacent-Monday rules that only show up years where the date has the corresponding weekday:

BEGIN:VEVENT
DTSTAMP:20130210T211949Z
UID:20130210-new-years-day-less-1@usa.gov
DTSTART;VALUE=DATE:20121231
DTEND;VALUE=DATE:20121231
SUMMARY:New Year’s Day (moved from a Saturday)
RRULE:FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=31
EXRULE:FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=31;BYDAY=MO,TU,WE,TH,SA,SU
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20130210T211949Z
UID:20130210-new-years-day-plus-1@usa.gov
DTSTART;VALUE=DATE:20130102
DTEND;VALUE=DATE:20130102
SUMMARY:New Year’s Day (moved from a Sunday)
RRULE:FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=2
EXRULE:FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=2;BYDAY=TU,WE,TH,FR,SA,SU
END:VEVENT

Together with the original event (you can similarly filter this one for BYDAY=MO,TU,WE,TH,FR if you don't want the actual holiday to show up in your feed), you cover all years without getting false positives on years where there was no need to move the time off to a weekday:

BEGIN:VEVENT
DTSTAMP:20130210T211949Z
UID:20130210-new-years-day@usa.gov
DTSTART;VALUE=DATE:20130101
DTEND;VALUE=DATE:20130101
SUMMARY:New Year’s Day
RRULE:FREQ=YEARLY;BYMONTH=1;BYMONTHDAY=1
END:VEVENT

Google Calendar happily consumes and understands my example iCalendar feed. Current iCal (and iOS devices synced from a Google Calendar that imported this feed) unfortunately are a little buggy (filed as  bug 13188350 [link probably only works for the reporter]), and fail to apply the specified weekday filters.

But hopefully that gets fixed too, some time soon.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top