Search code examples
icalendarrrule

iCalendar YEARLY rrule without BYMONTH value


I am building something that takes an icalendar event and calculate all occurrences of a repeating event.

I have discovered a problem which returns me a few YEARLY events every month. Those event has a yearly frequency but only have bymonthday without bymonth value.

Is this specified in the RFC-5545 or is this just a bug from my calendar tool. (PS: This is actually in my Google Calendar and this event displays correctly there)

BEGIN:VEVENT
DTSTART;VALUE=DATE:20110814
DTEND;VALUE=DATE:20110815
RRULE:FREQ=YEARLY;BYMONTHDAY=14
DTSTAMP:20170328T223152Z 
UID:388B4AE8602346DAA7BD9C1906FC390200000000000000000000000000000000
CREATED:20110610T073603Z
DESCRIPTION:
LAST-MODIFIED:20160811T230008Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Some BDAY
TRANSP:OPAQUE
CATEGORIES:http://schemas.google.com/g/2005#event
BEGIN:VALARM
ACTION:AUDIO
TRIGGER:-P1DT15H
ACKNOWLEDGED:20160811T230006Z
ATTACH;VALUE=URI:Basso
UID:26C6C0F7-8B77-4E20-8EFD-E82A614B0751
X-WR-ALARMUID:26C6C0F7-8B77-4E20-8EFD-E82A614B0751
END:VALARM
BEGIN:VALARM
ACTION:NONE
TRIGGER;VALUE=DATE-TIME:19760401T005545Z
END:VALARM
END:VEVENT

Solution

  • In the RFC 5545 - page 44 to be exact - the BYMONTHDAY is specified to expand a yearly rule, i.e. to generate more results that only 1 per year.

    Furthermore on the next page it gives an example of how a rrule must be evaluated:

    DTSTART;TZID=America/New_York:19970105T083000
    
      RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;
      BYMINUTE=30
    

    First, the "INTERVAL=2" would be applied to "FREQ=YEARLY" to arrive at "every other year". Then, "BYMONTH=1" would be applied to arrive at "every January, every other year". Then, "BYDAY=SU" would be applied to arrive at "every Sunday in January, every other year". Then, "BYHOUR=8,9" would be applied to arrive at "every Sunday in January at 8 AM and 9 AM, every other year". Then, "BYMINUTE=30" would be applied to arrive at "every Sunday in January at 8:30 AM and 9:30 AM, every other year".

    As you can see, even if FREQ is YEARLY, the rule will return "every Sunday in January at 8AM and 9AM, every other year", i.e. much more than 1 occurrence every two year. That is in a nutshell the concept of expanding the behavior of FREQ.

    Now applying to this rule:

    RRULE:FREQ=YEARLY;BYMONTHDAY=14

    It means "every 14th of the month, every year". So you will get 12 occurrences per year.

    Edit: That being said, after re-reading the RFC, the definition of BYMONTHDAY is:

    The BYMONTHDAY rule part specifies a COMMA-separated list of days of the month.

    This could be interpreted as "list of days of the month" as in "only the month specified by DTSTART property". However this interpretation does not make sense with other values of FREQ (MONTHLY, DAILY, etc.), so that would be a special case only for FREQ=YEARLY and BYMONTH is missing... Personally I do not believe it is correct, but I must admit the RFC is slightly ambiguous.

    Unfortunately there isn't any example of BYMONTHDAY with FREQ=YEARLY without BYMONTH, so there is no clear definite answer

    Conclusion: To avoid any problem you should add a BYMONTH if you want only "the 14th of a given month".

    Answer to your comment

    The rule "FREQ=MONTHLY;BYMONTHDAY=14" indeed produces the same result. The differences would appear with INTERVAL greater than 1, or other BYxxx.