In continuation of my last blog post for managing Calendars dynamically for the ExtJs Calendar component, I present in this blog post the logic behind parsing and serializing Recurrence rules for the Calendar component, if you have enabled recurrence for the Calendar (You get to specify Recurrence rules for an event in the Calendar component, only if you enable recurrence for it, i.e. enableRecurrence=true).
For the record, the Calendar component Recurrence support is based on iCal RRULE (details of which are available here: http://www.kanzaki.com/docs/ical/rrule.html).
Here’s a sample Recurrence rule based on options selected in the Calendar Panel:
FREQ=MONTHLY;INTERVAL=3;BYMONTHDAY=20;COUNT=5
The above Recurrence rule is a iCal RRule produced by the Calendar Panel, which basically means that the Event recurrence is set to monthly, with the event recurring every 3 months (i.e. the event happens after a gap of 3 months), with the event occurring on 20th date of the appropriate month (i.e. 20th of every 3rd month), and the event recurs 5 times (i.e. it occurs for 5 times). You can see how an iCal RRULE can capture complex recurrence patterns in simple strings.
Parsing these recurrence strings is also not that involved as it looks on the onset. Here’s a quick overview of the general properties of the recurrence rule string.
- A null or empty recurrence rule means the event is a one-time non-recurring event.
- The various parts of the recurrence rule are joined with a semi-colon.
- Each part of the recurrence rule is a key/value pair separated by an equals sign.
Now follows a quick description of the common parts of the rrule recurrence string, and then I would discuss properties specific for the recurrence frequency (i.e. properties specific for daily, weekly, monthly or yearly recurrence patterns). I will discuss the properties in context of the ExtJs CalendarPanel and hence only the properties supported by the CalendarPanel would be discussed.
- FREQ: This specifies the recurrence frequency (i.e. how often an event recurs). Supported values are DAILY, WEEKLY, MONTHLY and YEARLY with obvious meanings. This is the only required property if an event is recurring, all other properties for the recurrence rule are optional.
- INTERVAL: Specifies the interval between 2 occurrences of the event. e.g. FREQ=DAILY and INTERVAL=5 means that the event occurs every 5th day.
A very important point to note is that if user selected an Interval of 1 (for any frequency), this Interval attribute would be absent from the Recurrence string. So, you can safely assume a default of 1 if Interval is absent from the string. - COUNT: The number of times the event recurs.
e.g. FREQ=WEEKLY;INTERVAL=2;COUNT=3 means the event recurs every second week for 3 times (i.e. in total 6 weeks but every second week). - UNTIL: The end date for the event. The event recurs only until this date.
Note that COUNT and UNTIL properties are mutually exclusive. Only one of them can be specified (which makes sense, COUNT means the event occurs this many times, UNTIL means the event recurs until this date).
Now we have properties specific for each FREQ type.
- DAILY
This frequency does not have any specific property and only supports the above properties common for all frequency types. - WEEKLY
- BYDAY: This property specifies the days of the week the event should recur.
e.g. FREQ=WEEKLY;INTERVAL=3;BYDAY=MO,WE;COUNT=5 means that the event recurs every 3rd week on Monday and Wednesday and it has 5 recurrences spanning over 15 weeks (notice the interval is 3 meaning every 3rd week, so the total span is 15 weeks for COUNT=5).
If BYDAY is absent for a WEEKLY frequency, you can interpret that to mean for all days of the week. If specified, the value for this property for WEEKLY frequency would be a comma-separated list of first 2 characters for a week day corresponding to the days chosen on the UI.
- BYDAY: This property specifies the days of the week the event should recur.
- MONTHLY
For this frequency, you can choose if the event recurs on a fixed day of the month or a specified day of the specified week of the month. Accordingly, you have 2 mutually exclusive properties for this frequency.- BYMONTHDAY: This property means that the event recurs on a fixed day of the month.
e.g. FREQ=MONTHLY;INTERVAL=4;BYMONTHDAY=20;COUNT=5 means that the event recurs every 4 months, on 20th of each month and recurring for 5 times (spanning over 20 months). - BYDAY: This property means that he event recurs on a fixed day of the fixed week of the month.
e.g. FREQ=MONTHLY;INTERVAL=4;BYDAY=3MO;COUNT=5 means that the event recurs on 3rd Monday of every 4th month for 5 times.
Notice BYDAY here contains 2 parts, the first being the week of the month (between 1 and 5), and the second the day of the week (first 2 characters of the week day).
- BYMONTHDAY: This property means that the event recurs on a fixed day of the month.
- YEARLY
This frequency recurrence rule is a bit confusing containing a required and an optional property.
The required property is BYMONTH (containing the index of the month) and the optional is BYDAY. The interpretation of BYMONTH can vary slightly by the absence or presence of the BYDAY property. Let’s take an example first before discussing these 2 properties.i) FREQ=YEARLY;BYMONTH=12;COUNT=5
means that the event occurs every year in the month of December (BYMONTH=12 implies the month of December). The date of the month is decided by the start date of the event. If the event start date is 12th December, then the above rule means that the event occurs on 12th December of each year for 5 years.ii) FREQ=YEARLY;BYMONTH=12;BYDAY=3MO;COUNT=5
means that the event occurs on 3rd Monday in the month of December each year for 5 years.Here’s a more official description of the above properties:
- BYMONTH: This property specifies the month of the year (between 1 and 12). IF BYDAY is absent, then the day of the month is decided by the start date of the event in the specified month.
- BYDAY: This property means that the event recurs in the fixed day of the fixed week of the month specified by BYMONTH property. It again consists of 2 parts week of the month and day of the week (e.g. 3MO meaning 3rd Monday).
As another example, BYMONTH=5;BYDAY=2TU means the event recurs on 2nd Tueday of the month of May.
I know that above can be a bit perplexing at first, but if you take each frequency one by one, and write code for parsing the recurrence string for a frequency independently, it all becomes easy.
You can use the above description to also create the RRULE recurrence string from the description of a recurrence specified by the user through the UI of your application.
I myself have code for parsing iCal RRULE strings to Microsoft Exchange Managed API Recurrence objects, and vice versa (i.e. serializing an Exchange Managed API Recurrence object as a recurrence rule string). However, the code is non-public and hence cannot be shared.
Nevertheless, the above description should be enough for parsing and serializing iCal recurrence rules in any platform and to any server-side representation. Please let me know if you face an issue (I cannot provide the code, but can certainly advice if needed).
I am starting studying iCal specifications and I found your post very instructive.
Also the link to kanzaki was precious.
I think I put all pieces together but I must miss something.
I need to express a RRUL that will recurr every 45 days BEFORE the end of each trimester.
The following:
RRULE:FREQ=YEARLY;BYMONTH=3,6,9,12;BYMONTHDAY=-1
recurrs every end of each trimester.
The I thought it would be enough to “subtract” 45 days adding:
RRULE:FREQ=YEARLY;BYMONTH=3,6,9,12;BYMONTHDAY=-1;BYYEARDATE=-45
But such rule produces only one occurrence on 01/01/2012 (which is my DTSTART)
Where is the error and how can I set the correct RRULE for this kind of need?
Thanks
as the reference days (the end of each trimester) are at a fixed offset from then end of the year, as the resultant days (45 days before) is still in the same year, it’s possible to coin an ad-hoc solution using backward counting from the end of year.
RRULE:FREQ=YEARLY;BYYEARDAY=-321,-230,-138,-46
however, while being perfectly correct and validated, this RRULE is not understood by the main calendar application
Which main Calendar application are you referring to Peyre?
by adding BYYEARDAY=-45 (not BYYEARDATE)!
I’m trying to import a calendar event from an ics file into iCal/Calendar:
Event details
ics file
Hello
I have changed to UTC time format but still the problem is here..
Hi Anupama, you need to explicitly specify a DTEND also.
Hi there,
I got an essential question concerning RRULEs. My own parser / generator for RRULEs is finished, but only for “normal” recurrencies. Exceptions from a recurrence rule work fine (EXDATE), but what about changes (what is done by the INSTANCE in MAPI recurrence objects)? In our application you can modify one single appointment of a series, what is possible in Outlook as well (i.e. one of the ten appointments is at 4:00 pm instead of 1:00 pm). So can this be handled by RRULE strings? In the ICAL spec I didn’t find anything about this…
Thanks for your quick answer.
In the end I found it in the ICAL specs. You have to work with single ICALS for each exception using the recurrence ID. Its quite the same as the “instance thing”.
http://www.ietf.org/rfc/rfc2445.txt
4.8.4.4 Recurrence ID
Interestingly, I do not see my own comment here. Thanks for sharing the link anyways.
First off, thank you for posting this. It has been very useful but I have one concern. Can you please explain why you think they BYMONTHDAY and BYDAY rules are mutually exclusive for the MONTHLY frequency? I can kind of see what why you might think this but this isn’t explicitly mentioned in the specification and I could think of a case where having both is valid. For example,
David, I am not sure I understand your use-case completely. Can you please elaborate?
Hi,
First of all thanks for the wonderful and helpful information. I implemented rrule in my application and it is working find but
start time and end time is totally differnt from the input i have given.other things are working fine.below is the code
Convert.ToDateTime(strStarttime).ToUniversalTime().ToString(“yyyyMMdd\\THHmmss\\Z”) .where strStarttime is 3:20 PM like that.
can anyone tell me what is wrong here?
Timezone. You need to factor-in the Timezone Sanchi (TZID).