RFC 7529

Non-Gregorian Recurrence Rules in the Internet Calendaring and Scheduling Core Object Specification (iCalendar)

๐Ÿ“… published May 2015, from ietf

Non-Gregorian Recurrence Rules in the Internet Calendaring and Scheduling Core Object Specification (iCalendar) ๐ŸŽ€

  • RFC number: RFC 7529 ๐Ÿ“œ
  • Authors: C. Daboo (Apple Inc.) and G. Yakushev (Google Inc.) ๐ŸŽ๐Ÿ”
  • Category: Standards Track โœจ
  • ISSN: 2070-1721
  • Date: May 2015 ๐Ÿ—“๏ธ
  • Updates: RFC 5545, RFC 6321, and RFC 7265 ๐Ÿ”

okay so this doc is about getting non-Gregorian recurrence rules into iCalendar, the format we all use for calendar data ๐Ÿ“… it's giving "we need more calendars in our lives." ๐Ÿ’–

Abstract โœจ

okay so this document extends iCalendar (RFC 5545) so it can handle non-Gregorian recurrence rules ๐ŸŒธ it also explains how CalDAV (RFC 4791) servers and clients can be extended to support these new rules, which is so iconic ๐Ÿ™Œ

Status of This Memo ๐Ÿ“‹

this is an Internet Standards Track document ๐Ÿฆ‹

it's a product of the Internet Engineering Task Force (IETF), so it represents the consensus of the IETF community ๐Ÿซถ it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). more info on Internet Standards is in Section 2 of RFC 5741 ๐Ÿ’…

info about the current status of this doc, any errata, and how to give feedback is at http://www.rfc-editor.org/info/rfc7529 ๐Ÿ”—

Copyright (c) 2015 IETF Trust and the persons identified as the document authors. All rights reserved. ๐ŸŒˆ

this document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) as in effect on the date of publication. please read these carefully, because they describe your rights and restrictions for this doc ๐Ÿง code components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they're provided without warranty as described in the Simplified BSD License. ๐Ÿซง

Table of Contents ๐Ÿ—‚๏ธ

1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   2
2.  Conventions Used in This Document . . . . . . . . . . . . . .   3
3.  Overview  . . . . . . . . . . . . . . . . . . . . . . . . . .   4
4.  Extended RRULE Property . . . . . . . . . . . . . . . . . . .   6
  4.1.  Skipping Invalid Dates  . . . . . . . . . . . . . . . . .   6
  4.2.  Handling Leap Months  . . . . . . . . . . . . . . . . . .   9
  4.3.  Examples  . . . . . . . . . . . . . . . . . . . . . . . .  10
5.  Registering Calendar Systems  . . . . . . . . . . . . . . . .  13
6.  Compatibility . . . . . . . . . . . . . . . . . . . . . . . .  13
7.  Use with iTIP . . . . . . . . . . . . . . . . . . . . . . . .  14
8.  Use with xCal . . . . . . . . . . . . . . . . . . . . . . . .  15
9.  Use with jCal . . . . . . . . . . . . . . . . . . . . . . . .  15
10. Use with CalDAV . . . . . . . . . . . . . . . . . . . . . . .  16
  10.1.  CALDAV:supported-rscale-set Property . . . . . . . . . .  17
11. Security Considerations . . . . . . . . . . . . . . . . . . .  18
12. References  . . . . . . . . . . . . . . . . . . . . . . . . .  18
  12.1.  Normative References . . . . . . . . . . . . . . . . . .  18
  12.2.  Informative References . . . . . . . . . . . . . . . . .  19
Appendix A.  xCal RELAX NG Schema Update  . . . . . . . . . . . .  20
Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . .  21
Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  21

1. Introduction ๐ŸŒท

okay so iCalendar [RFC5545] is everywhere, and it represents dates and times using only the Gregorian calendar system ๐Ÿ“† it does have a way to use other calendar systems through a CALSCALE property, but that has never actually been used ๐Ÿ™ˆ still, we need at least non-Gregorian recurrence patterns for anniversaries and lots of local, religious, or civil holidays that are based on non-Gregorian dates ๐ŸŽ‰

using the existing CALSCALE property for non-Gregorian calendars has a couple of problems, bestie:

  1. The CALSCALE property lives in the top-level VCALENDAR object, so it applies to every component inside that object. in today's multi-cultural world, that's a big restriction, because you can't mix events from different calendar systems in the same iCalendar object. for example, you couldn't have both the Gregorian New Year and the Chinese New Year in one object ๐Ÿงง
  2. Time zone and daylight saving time rules are usually published using Gregorian dates and rules (like "the 3rd Sunday in March"), so they get converted into VTIMEZONE components that use Gregorian dates and recurrence rules. that means the VTIMEZONE is stuck on the Gregorian calendar, but a VEVENT might want a different non-Gregorian calendar scale. so the single top-level CALSCALE property is still not enough ๐Ÿ˜ญ

this spec fixes that by keeping CALSCALE set to Gregorian and redefining the RRULE recurrence rule property so it accepts new items, including one that lets non-Gregorian calendar systems be used ๐Ÿ’… so all the DATE, DATE-TIME, and PERIOD values in the iCalendar object stay in the Gregorian calendar system, but repeating patterns can be defined in other calendar systems too โœจ it's then up to calendar user agents and servers to map between Gregorian and non-Gregorian calendar systems when they expand recurrence instances ๐Ÿงš the non-Gregorian recurrence rules can be used in any iCalendar component that allows the RRULE property, including VTIMEZONE components (to allow for possible future use of non-Gregorian rules in published daylight saving time data) ๐ŸŒˆ

this spec doesn't define calendar systems itself ๐Ÿ™…โ€โ™€๏ธ instead, it uses the calendar system registry defined by the Unicode Consortium in their Common Locale Data Repository (CLDR) project [UNICODE.CLDR], as implemented in the Unicode (International Components for Unicode (ICU)) Library [UNICODE.ICU] ๐Ÿซถ

this spec makes the following updates, and it's giving organized:

It updates iCalendar [RFC5545], the XML format for iCalendar (xCal) [RFC6321], and the JSON format for iCalendar (jCal) [RFC7265], to extend the RRULE property definition. It clarifies use of the iCalendar Transport-Independent Interoperability Protocol (iTIP) [RFC5546] to specify how the extended RRULE property should be handled in iTIP messages. It extends CalDAV [RFC4791] to specify how the extended RRULE property can be supported by CalDAV servers and clients.

2. Conventions Used in This Document ๐Ÿ’œ

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this doc are to be interpreted as described in [RFC2119] ๐Ÿง 

the notation in this memo is the ABNF notation of [RFC5234], as used by iCalendar [RFC5545] โœ๏ธ any syntax elements shown below that aren't explicitly defined in this spec come from iCalendar [RFC5545], iTIP [RFC5546], and CalDAV [RFC4791] ๐Ÿ”

when XML element types in the namespaces "DAV:" and "urn:ietf:params:xml:ns:caldav" are referenced outside of an XML fragment, the strings "DAV:" and "CALDAV:" get prefixed to the element type names, respectively ๐Ÿท๏ธ

when a Gregorian calendar date is shown in text, it uses the format YYYYMMDD, where YYYY is the 4-digit year, MM is the 2-digit month, and DD is the 2-digit day (same format as iCalendar [RFC5545]) ๐Ÿ“… the Chinese calendar is used as the example of a non-Gregorian calendar for illustration ๐Ÿงง when a Chinese calendar date is shown in text, it uses the format {C}YYYYMM[L]DD. so it's the same as Gregorian but with a {C} prefix, plus an optional L after the month to mark a leap month ๐ŸŒ™ similarly, {E} and {H} are used in other examples as prefixes for Ethiopic (Amete Mihret) and Hebrew dates ๐Ÿ‡ช๐Ÿ‡น๐Ÿ•Ž

the {} prefix is purely for illustration and never shows up in actual dates used in iCalendar or related specs ๐Ÿซง also, the Chinese calendar years in the examples are based on the Unicode (ICU) [UNICODE.ICU] library's Chinese calendar epoch. there are several different Chinese calendar epochs in common use, but picking one over another doesn't change the actual calculation of the Gregorian equivalent dates, as long as the conversion is always done with the same epoch ๐Ÿซถ

to be continued... ๐ŸŽ€โœจ

3. Overview ๐ŸŒธ

okay so the Gregorian calendar, the one we all use, has 12 months ๐Ÿ’… Each month has a fixed number of days, either 30 or 31, except February, which has 28 or 29 (29 in a leap year). Weeks are 7 days long, with the names Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, and Sunday. A year can be 365 or 366 days (366 in a leap year). The number of whole weeks in a year is 52, but the [ISO.8601.2004] week numbering scheme that iCalendar [RFC5545] uses can count up to 53 โœจ

In iCalendar, the "RECUR" value type has a bunch of fields for describing a recurrence pattern, and those fields get their limits from the Gregorian calendar. other calendar systems can have different limits and different behaviours that need to be handled, so this specification doesn't cover the maximum values for the elements in the "RECUR" value ๐Ÿฆ‹

So how do we generate recurring instances in a non-Gregorian calendar? We use these principles:

  1. iCalendar data keeps using the "GREGORIAN" calendar system, so all "DATE", "DATE-TIME", and "PERIOD" values keep using the Gregorian format and limits ๐ŸŽ€
  2. The "RRULE" property gets extended with an "RSCALE" element in its value, which says which calendar system to use for the recurrence pattern. The existing elements of the "RRULE" value type are still used, but they're modified to support different upper limits based on the "RSCALE" value. Month numbers are also modified so a leap month can be specified ๐ŸŒˆ All the existing requirements for "RRULE" still apply (for example, the "RRULE" has to match the "DTSTART" value of the master instance). Other recurrence properties, like "RECURRENCE-ID", "RDATE", and "EXDATE", keep using the Gregorian date format, because "CALSCALE" is unchanged ๐Ÿ’œ

When generating instances, the following procedure might be used:

  1. Convert the "DTSTART" property value of the master recurring component into the date and time components for the calendar system specified by the "RSCALE" element in the "RRULE" value. This gives you the "seed" value for generating the later recurrence instances ๐ŸŒฑ
  2. Iteratively generate instances by applying the "RRULE" value to the year, month, and day components of the date in the new calendar system ๐Ÿ”
  3. For each generated instance, convert the dates and times back from the non-Gregorian form into Gregorian, and use those values for other properties like "RECURRENCE-ID" ๐Ÿซถ

Let's look at an example for an event that's the Chinese New Year ๐Ÿงง

DTSTART;VALUE=DATE:20130210
RRULE:RSCALE=CHINESE;FREQ=YEARLY
SUMMARY:Chinese New Year

To generate the instances, first the "DTSTART" value "20130210" gets converted into the Chinese calendar system, giving "{C}46500101". Next, the year component is incremented by one to give "{C}46510101", and that gets converted back into Gregorian as "20140131". More instances are generated by repeatedly increasing the year component in the Chinese date and converting back to Gregorian ๐ŸŽ‰


4. Extended RRULE Property โœจ

okay bestie, this section is where the actual magic happens ๐Ÿ”ฎ This specification extends the existing "RRULE" iCalendar property value with a new "RSCALE" element, which says which calendar system is used to generate the recurrence pattern ๐Ÿ’–

When "RSCALE" is present, the other changes to "RRULE" are:

  1. Elements that include numeric values (for example, "BYYEARDAY") have numeric ranges defined by the "RSCALE" value. This is because some calendar systems have more than 366 days in a year ๐Ÿ“…
  2. Month numbers can include an "L" suffix to show that the specified month is a leap month in the corresponding calendar system (see Section 4.2) ๐ŸŒ™
  3. A "SKIP" element is added to define how "missing" instances are handled (see Section 4.1) ๐Ÿฆ‹

The syntax for the "RECUR" value gets modified like this:

; recur-rule-part is extended from RFC 5545
recur-rule-part =/   ("RSCALE" "=" rscale)
                   / ("SKIP" "=" skip)

rscale          = (iana-token  ; A CLDR-registered calendar system
                               ; name.
                 / x-name)     ; A non-standard, experimental
                               ; calendar system name.
                               ; Names are case insensitive,
                               ; but uppercase values are preferred.

skip            = ("OMIT" / "BACKWARD" / "FORWARD")
                 ; Optional, with default value "OMIT", and
                 ; MUST NOT be present unless "RSCALE" is present.

monthnum        = 1*2DIGIT  ["L"]
                 ; Existing element modified to include a leap
                 ; month indicator suffix.

4.1. Skipping Invalid Dates ๐Ÿ™ˆ

In every calendar system, only certain combinations of day-of-month and month are valid for a given year. For example, in the Gregorian calendar, January 31st is valid but February 31st is not. Similarly, February 29th is valid in a leap year but invalid in a non-leap year. Other calendar systems can also include leap months (see Section 4.2), which vary from year to year. This is a problem for recurring events, because the frequency of recurrence might give rise to an invalid date. For example, a recurring event that starts on January 31st and repeats monthly will generate invalid dates for months with fewer than 31 days ๐Ÿ’€

The iCalendar [RFC5545] specification requires recurrence rule generators to ignore any invalid dates generated when iterating the rule. However, that behaviour might be surprising to a calendar user born on a leap day, whose birthday event only shows up on their calendar every four years ๐ŸŽ‚ Humans have common conventions for what to do in these cases, but those conventions can differ from calendar system to calendar system, and even within the same calendar system, depending on the nature of the event. Typically, humans expect the "missing" events to be moved to an earlier or later (valid) date ๐Ÿฅน

This specification introduces a new "RRULE" element, "SKIP", for use only when the "RSCALE" element is present. The "SKIP" element lets the calendar user agent specify new options for handling invalid dates ๐Ÿงš

  • "SKIP=OMIT": this is the default option, and it corresponds to the existing iCalendar behaviour of simply ignoring the invalid date.
  • "SKIP=BACKWARD": when this option is set, a date with an invalid month is changed to the previous (valid) month. A date with an invalid day-of-month is changed to the previous (valid) day-of-month.
  • "SKIP=FORWARD": when this option is set, a date with an invalid month is changed to the next (valid) month. A date with an invalid day-of-month is changed to the next (valid) day-of-month.

Note that for both "BACKWARD" and "FORWARD", if the month is changed and that results in an invalid day-of-month, then the skip behaviour gets reapplied as per the day-of-month rules, according to the processing order defined below ๐Ÿ”„

The month and day-of-month skip behaviour is only applied at specific points during the processing of an "RRULE". Those points are decided by the order in which any "BYxxx" elements are processed. The order is as follows (based on the "RRULE" element processing order defined in Section 3.3.10 of [RFC5545]) ๐Ÿ“‹

  • BYMONTH
  • SKIP (for invalid month only)
  • BYWEEKNO
  • BYYEARDAY
  • BYMONTHDAY
  • SKIP (for invalid day)
  • BYDAY
  • BYHOUR
  • BYMINUTE
  • BYSECOND
  • BYSETPOS
  • COUNT
  • UNTIL

It's often possible to avoid dealing with invalid dates altogether by figuring out what a human user really means ๐Ÿง  For example, a human creating a monthly recurring event that starts on January 31st probably wants the event on the last day of every month. In that case, you could encode it into an "RRULE" using the "BYMONTHDAY=-1" element ๐Ÿ’…

Only a few types of recurrence patterns are likely to need "SKIP". Here's a list of some situations where it might be needed:

  1. The start date of the recurrence is a leap day in the specified calendar system.
  2. The start date of the recurrence is in a leap month in the specified calendar system.
  3. The start date of the recurrence has a day-of-month value greater than the smallest day-of-month value for any month in any year in the specified calendar system.
  4. A "BYMONTHDAY" element in an "RRULE" has a day-of-month value greater than the smallest day-of-month value for any month in any year in the specified calendar system.
  5. A "BYMONTH" element in an "RRULE" has a value corresponding to a leap month in the specified calendar system.
  6. A combination of a "BYMONTHDAY" element and a "BYMONTH" element in an "RRULE" has a value corresponding to a leap day in the specified calendar system.
  7. A "BYYEARDAY" element in an "RRULE" has an absolute value greater than the smallest number of days in any year in the specified calendar system.
  8. A "BYWEEKNO" element in an "RRULE" has an absolute value greater than the smallest number of weeks in any year in the specified calendar system.

Examples of using "SKIP" for some common use cases appear in Section 4.3 ๐ŸŽ€

4.2. Handling Leap Months ๐ŸŒ•

Leap months can occur in different calendar systems. For those calendar systems, the following rules apply when "identifying" months:

  1. Numeric values 1 through N are used to identify regular, non-leap, months (where N is the number of months in a regular, non-leap, year).
  2. The suffix "L" is added to the regular month number to indicate a leap month that follows the regular month. For example, "5L" is a leap month that follows the 5th regular month in the year.

Be careful when mapping the month identifiers used here to those of any underlying calendar system library you're using ๐Ÿšจ In particular, the Hebrew calendar system used by Unicode (ICU) [UNICODE.ICU] uses a month number scheme of 1 through 13, with month 6 being the leap month. In non-leap years, month 6 is skipped. So ICU months 1 through 5 map to iCalendar months 1 through 5, ICU month 6 maps to iCalendar month "5L", and ICU months 7 through 13 map to iCalendar months 6 through 12 ๐Ÿ’œ

4.3. Examples โœจ

4.3.1. Chinese New Year ๐Ÿงง

Okay, so check out this set of iCalendar properties (from the example above) ๐Ÿ’–

DTSTART;VALUE=DATE:20130210
RRULE:RSCALE=CHINESE;FREQ=YEARLY
SUMMARY:Chinese New Year

These make a recurring event for the Chinese New Year, and the first instance is the one in Gregorian year 2013 ๐ŸŽ€

The Chinese date for that first instance is {C}46500101. The table below shows the first instance plus the next four. Each one comes from adding the right amount to the year part of the Chinese date. There's also the conversion back to the Gregorian date โœจ

            +--------------+--------------------------+
            | Chinese Date | Gregorian Date           |
            +--------------+--------------------------+
            | {C}46500101  | 20130210 - DTSTART value |
            | {C}46510101  | 20140131                 |
            | {C}46520101  | 20150219                 |
            | {C}46530101  | 20160208                 |
            | {C}46540101  | 20170128                 |
            +--------------+--------------------------+

4.3.2. Ethiopic 13th Month ๐ŸŒธ

Here's the next one, bestie ๐Ÿฆ‹

DTSTART;VALUE=DATE:20130906
RRULE:RSCALE=ETHIOPIC;FREQ=MONTHLY;BYMONTH=13
SUMMARY:First day of 13th month

These make a recurring event for the first day of the 13th month, and the first instance is the one in Gregorian year 2013. There are a bunch of other ways to write that RRULE and get the same dates, but this one was picked on purpose to show off a BYMONTH value going past 12, which is something iCalendar originally didn't allow (Section 3.3.10 of [RFC5545]). Iconic behavior ๐Ÿ‘‘

The Ethiopic date for the first instance is {E}20051301. The table below shows that instance plus the next four, each made by adding the right amount to the year part of the Ethiopic date. Also included is the conversion back to Gregorian ๐Ÿ’…

              +---------------+--------------------------+
              | Ethiopic Date | Gregorian Date           |
              +---------------+--------------------------+
              | {E}20051301   | 20130906 - DTSTART value |
              | {E}20061301   | 20140906                 |
              | {E}20071301   | 20150906                 |
              | {E}20081301   | 20160906                 |
              | {E}20091301   | 20170906                 |
              +---------------+--------------------------+

Note that in this example, the BYMONTH part of the RRULE matches the Ethiopic month number and not the Gregorian month ๐Ÿ™ˆ

4.3.3. Hebrew Anniversary Starting in a Leap Month ๐ŸŒˆ

Consider this set of iCalendar properties ๐Ÿงš

DTSTART;VALUE=DATE:20140208
RRULE:RSCALE=HEBREW;FREQ=YEARLY;BYMONTH=5L;BYMONTHDAY=8;SKIP=FORWARD
SUMMARY:Anniversary

These make a recurring event for the 8th day of the Hebrew month Adar I (the leap month marked by 5L), and the first instance is the one in Gregorian year 2014 ๐Ÿ’œ

The Hebrew date for the first instance is {H}577405L08, which is a leap month in year 5774. The table below shows that instance plus the next four. Each one comes from adding the right amount to the year part of the Hebrew date, and the fact that only year 5776 is a leap year matters here. So in the other years, the Hebrew month part gets pushed forward to month 6. The conversion back to Gregorian is included too ๐Ÿ”ฅ

            +--------------+--------------------------+
            | Hebrew Date  | Gregorian Date           |
            +--------------+--------------------------+
            | {H}577405L08 | 20140208 - DTSTART value |
            | {H}57750608  | 20150227                 |
            | {H}577605L08 | 20160217                 |
            | {H}57770608  | 20170306                 |
            | {H}57780608  | 20180223                 |
            +--------------+--------------------------+

4.3.4. Gregorian Leap Day with SKIP ๐Ÿ’€

Consider this set of iCalendar properties (this one's so relatable ๐Ÿ˜ญ)

DTSTART;VALUE=DATE:20120229
RRULE:FREQ=YEARLY
SUMMARY:Anniversary

These make a recurring event for February 29th, 2012, in the standard iCalendar calendar scale, which is Gregorian. The normal iCalendar behavior is that dates that don't exist in a recurrence set just get ignored. So these properties would only produce instances in leap years (2016, 2020, and so on), which is probably not what users are hoping for ๐Ÿฅบ The new RSCALE option from this spec comes with a SKIP element that can "fill in" those missing instances in a sensible way. The set below does exactly that ๐ŸŽ€

DTSTART;VALUE=DATE:20120229
RRULE:RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD
SUMMARY:Anniversary

With these properties, the "missing" instances in non-leap years land on March 1st of those years ๐ŸŒŸ

  +-------------------------------+----------------------------+
  | Instances (with SKIP=FORWARD) | Instances (without RSCALE) |
  +-------------------------------+----------------------------+
  | 20120229                      | 20120229 - DTSTART value   |
  | 20130301                      |                            |
  | 20140301                      |                            |
  | 20150301                      |                            |
  | 20160229                      | 20160229                   |
  | 20170301                      |                            |
  +-------------------------------+----------------------------+

5. Registering Calendar Systems ๐Ÿ—‚๏ธ

This spec uses the Unicode Consortium's registry of calendar systems [UNICODE.CLDR] to decide which values are valid for the RSCALE element of an RRULE. Heads up: the underscore character _ never shows up in CLDR-based calendar system names. New values can be added to this registry by following Unicode Consortium rules. A lot of non-Gregorian calendar implementations are going to use the software libraries from Unicode (ICU) [UNICODE.ICU], so it makes total sense to reuse their registry instead of making a brand new one ๐Ÿง  RSCALE values are case insensitive, but uppercase is the preferred look ๐Ÿ’…

CLDR also supports "alias" values, which are alternative names for certain calendar systems. These aliases can be used as RSCALE values, and they get treated exactly like the CLDR calendar system they alias โœจ

When you're using CLDR data, calendar agents SHOULD take the "deprecated" value into account and switch over to the "preferred" calendar system instead. For example, the islamicc calendar system counts as deprecated in favor of islamic-civil ๐ŸŒ™

6. Compatibility ๐Ÿซถ

For calendar user agents that don't support the RSCALE element, or that don't support the calendar system named by the RSCALE element value, these are the possible behaviors when processing iCalendar data:

  1. The calendar user agent can reject the entire iCalendar object that contains at least one iCalendar component using the unrecognized RSCALE element or element value.
  2. The calendar user agent can reject just the iCalendar components that contain an unrecognized RSCALE element or element value. Note that any overridden components tied to those rejected components MUST also be rejected (meaning any other components sharing the same UID property value as the one with the unrecognized RSCALE element or element value).
  3. The calendar user agent can fall back to non-recurring behavior for the iCalendar component that contains the unrecognized RSCALE element or element value (basically ignoring the RRULE property). However, any overridden components SHOULD be rejected, since they'd be "orphaned" instances that look totally out of place ๐Ÿ™ƒ

In general, option (2) above is the best choice for a calendar user agent, since it's the least disruptive one ๐Ÿฆ‹ Note that when processing iTIP [RFC5546] messages, the way the rejection happens is covered in the next section ๐Ÿ’œ

7. Use with iTIP ๐Ÿ“จ

iTIP [RFC5546] defines how iCalendar data can be sent between calendar user agents so calendar components can be scheduled between calendar users. A lot of the time you can't know what a calendar user agent receiving an iTIP message can do, but iTIP has fallback behavior for those situations ๐ŸŒธ

For calendar user agents that don't support the RSCALE element, here's what can happen when iTIP messages with an RSCALE element arrive:

  • The receiving calendar user agent can reject the entire iTIP message and send back an iTIP reply with a REQUEST-STATUS property set to the 3.1 status code (as per Section 3.6.14 of [RFC5546]).
  • The receiving calendar user agent can fall back to non-recurring behavior for the calendar component (basically ignoring the RRULE property), and send back an iTIP reply with a REQUEST-STATUS property set to the 2.3, 2.5, 2.8, or 2.10 status codes (as per Sections 3.6.4, 3.6.6, 3.6.9, or 3.6.11, respectively, of [RFC5546]).

For calendar user agents that do support the RSCALE element but don't support the calendar system named by the RSCALE element value, here's what can happen:

  • The iTIP message SHOULD be rejected, with a REQUEST-STATUS property set to the 3.1 status code (as per Section 3.6.14 of [RFC5546]).
  • If the iTIP message is accepted and the calendar component is treated as non-recurring, an iTIP reply with a REQUEST-STATUS property set to the 2.8 or 2.10 status codes (as per Sections 3.6.9 or 3.6.11, respectively, of [RFC5546]) SHOULD be sent back ๐ŸŽ€

As noted in Section 6, the best choice is to reject the entire iTIP message ๐Ÿ‘‘

8. Use with xCal ๐Ÿง

xCal [RFC6321] defines how iCalendar data is represented in XML. This spec extends the <recur> XML element from Section 3.6.10 of [RFC6321] like so:

  1. A new <rscale> XML element is defined as a child element of <recur>. The content of this element MUST be a string whose value is the RSCALE element value of the RRULE, with case preserved.
  2. A new <skip> XML element is defined as a child element of <recur>. The content of this element MUST be a string whose value is the SKIP element value of the RRULE, with case preserved.
  3. The <bymonth> XML element is redefined to support either numeric or string values as its content (as per Section 4.2).

Extensions to the RELAX NG schema in Appendix A of [RFC6321] are defined in Appendix A of this document ๐Ÿงš

Example: the iCalendar RRULE property:

RRULE:RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD

would be represented in XML as:

<rrule>
  <recur>
    <rscale>GREGORIAN</rscale>
    <freq>YEARLY</freq>
    <skip>FORWARD</skip>
  </recur>
</rrule>

9. Use with jCal โœจ

okay so jCal [RFC7265] is the JSON way of writing iCalendar data ๐Ÿ’– and this spec glams up the "recur" JSON object from Section 3.6.10 of [RFC7265] in three ways:

  1. A new "rscale" child member is defined. It MUST be a string whose value is the "RSCALE" element value of the "RRULE", with case preserved.
  2. A new "skip" child member is defined. It MUST be a string whose value is the "SKIP" element value of the "RRULE", with case preserved.
  3. The "bymonth" child member is redefined to support either numeric or string values. If the "BYMONTH" element value is an integer, then a numeric JSON value MUST be used. If the "BYMONTH" element value is an integer with the "L" suffix (as per Section 4.2), then a JSON string value MUST be used.

Example: the iCalendar "RRULE" property:

RRULE:RSCALE=GREGORIAN;FREQ=YEARLY;SKIP=FORWARD

would be represented in JSON as:

[
  "rrule",
  {},
  "recur",
  {
    "rscale": "GREGORIAN",
    "freq": "YEARLY",
    "skip": "FORWARD"
  }
]

10. Use with CalDAV ๐Ÿฆ‹

the CalDAV [RFC4791] calendar access protocol lets clients and servers swap iCalendar data ๐ŸŽ€ and CalDAV clients can also query the calendar data stored on the server, including time-based queries. since an "RSCALE" element value decides the time ranges for recurring instances in a calendar component, CalDAV servers need to support it so they can interoperate with clients that are also using the "RSCALE" element. no cap, this part matters a lot ๐Ÿ˜ญ

a CalDAV server advertises a CALDAV:supported-rscale-set Web Distributed Authoring and Versioning (WebDAV) property on calendar home or calendar collections if it supports use of the "RSCALE" element as described in this specification. the server can advertise a specific set of supported calendar systems by including one or more CALDAV:supported-rscale XML elements within the CALDAV:supported-rscale-set XML element. if no CALDAV:supported-rscale XML elements are included in the WebDAV property, then clients can try any calendar system value but need to be prepared for a failure when attempting to store the calendar data. ๐Ÿ’…

Clients MUST NOT attempt to store iCalendar data containing "RSCALE" elements if the CALDAV:supported-rscale-set WebDAV property is not advertised by the server. ๐Ÿšซ

the server SHOULD return an HTTP 403 response with a DAV:error element containing a CALDAV:supported-rscale XML element, if a client attempts to store iCalendar data with an "RSCALE" element value not supported by the server. โœจ

it's possible for an "RSCALE" value to be present in calendar data on the server being accessed by a client that doesn't support an "RSCALE" element or its specified value. it's expected that existing clients, unaware of "RSCALE", will fail gracefully by ignoring the calendar component, while still processing other calendar data on the server (as per option (2) in Section 6). iconic behavior honestly ๐ŸŒธ

10.1. CALDAV:supported-rscale-set Property ๐Ÿงš

Name: supported-rscale-set

Namespace: urn:ietf:params:xml:ns:caldav

Purpose: Enumerates the set of supported iCalendar "RSCALE" element values supported by the server.

Protected: This property MUST be protected and SHOULD NOT be returned by a PROPFIND allprop request (as defined in Section 14.2 of [RFC4918]).

Description: See above.

Definition:

<!ELEMENT supported-rscale-set (supported-rscale*)>
<!ELEMENT supported-rscale (#PCDATA)>
<!-- PCDATA value: string - case insensitive but
     uppercase preferred -->

Example:

<C:supported-rscale-set
     xmlns:C="urn:ietf:params:xml:ns:caldav">
  <C:supported-rscale>GREGORIAN</C:supported-rscale>
  <C:supported-rscale>CHINESE</C:supported-rscale>
  <C:supported-rscale>ISLAMIC-CIVIL</C:supported-rscale>
  <C:supported-rscale>HEBREW</C:supported-rscale>
  <C:supported-rscale>ETHIOPIC</C:supported-rscale>
</C:supported-rscale-set>

11. Security Considerations ๐Ÿ”ฅ

๐Ÿšจ bestie, security is the main character here, so please read this one properly ๐Ÿšจ

This specification does not introduce any additional security concerns beyond those described in [RFC5545], [RFC5546], and [RFC4791]. ๐Ÿซถ

12. References ๐Ÿ“š

12.1. Normative References

12.2. Informative References

  • [ISO.8601.2004] International Organization for Standardization, "Data elements and interchange formats - Information interchange - Representation of dates and times", ISO Standard 8601, December 2004.
  • [UNICODE.ICU] "International Components for Unicode", April 2014, <http://site.icu-project.org>.

Appendix A. xCal RELAX NG Schema Update ๐ŸŽ€

okay so the following changes are made to the RELAX NG schema defined in Appendix A of [RFC6321] ๐Ÿ’–

# 3.3.10 RECUR
# This extension adds type-rscale and type-skip,
# and modifies type-bymonth

value-recur = element recur {
    type-rscale?,
    type-freq,
    (type-until | type-count)?,
    element interval {
        xsd:positiveInteger
    }?,
    type-bysecond*,
    type-byminute*,
    type-byhour*,
    type-byday*,
    type-bymonthday*,
    type-byyearday*,
    type-byweekno*,
    type-bymonth*,
    type-bysetpos*,
    element wkst { type-weekday }?,
    type-skip?
}


type-rscale = element rscale {
    xsd:string
}

type-bymonth = element bymonth {
    xsd:positiveInteger |
    xsd:string
}

type-skip = element skip {
    "OMIT" |
    "BACKWARD" |
    "FORWARD"
}

Acknowledgments ๐ŸŒˆ

thanks to the following for feedback: Mark Davis, Mike Douglass, Donald Eastlake, Peter Edberg, Marten Gajda, Philipp Kewisch, Barry Leiba, Jonathan Lennox, Ken Murchison, Arnaud Quillaud, Dave Thewlis, and Umaoka Yoshito. ๐Ÿซถ

this specification originated from work at the Calendaring and Scheduling Consortium, which has helped with the development and testing of implementations. the real MVPs ๐Ÿ‘‘

Authors' Addresses ๐Ÿ’œ

Cyrus Daboo
Apple Inc.
1 Infinite Loop
Cupertino, CA  95014
United States

EMail: cyrus@daboo.name
URI:   http://www.apple.com/


Gregory Yakushev
Google Inc.
Brandschenkestrasse 100
8002 Zurich
Switzerland

EMail: gyakushev@yahoo.com
URI:   http://www.google.com/
๐Ÿ“ฆ where this page comes from