0

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?

Auberon Vacher
  • 4,655
  • 1
  • 24
  • 36
ecmanaut
  • 5,030
  • 2
  • 44
  • 66

2 Answers2

1

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.

Community
  • 1
  • 1
Auberon Vacher
  • 4,655
  • 1
  • 24
  • 36
  • Thanks! I'll have to try playing around with and confirming this tonight, but you are definitely right about the RFC deprecation; I missed that part. – ecmanaut Feb 11 '13 at 12:15
  • The New Year's Day rule looks like it needs adjustment though; if Jan 1 is a Saturday, the event should show up on Friday Dec 31 of the preceding year, if Sunday then Monday Jan 2; never on Jan 3. But I haven't wrapped my mind around the logic you're shooting for, so I could be missing some subtlety. – ecmanaut Feb 11 '13 at 12:22
  • for New Year's days (USA) 2 RRULE are needed, see above the answer for more details – Auberon Vacher Feb 11 '13 at 14:00
  • I think they always are, as we want the middle (not first or last) date to take precedence, when it's on a weekday. I've updated your answer with some extras at the end – if you don't see it yet, wait a bit before you edit the answer again, and the end result will be all the better (I linked RFC-compliant examples for both standards). Hm. I guess BYMONTHDAY=4,3,5;BYSETPOS=1 would probably work too, though. – ecmanaut Feb 12 '13 at 07:23
  • Nope, two rules are needed - 4,3,5 didn't work for either order of BYMONTHDAY and BYDAY, which I seem to recall is in accordance with the precedence rules stated in the spec. – ecmanaut Feb 12 '13 at 08:16
0

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.

ecmanaut
  • 5,030
  • 2
  • 44
  • 66