Summary |  Admin |  Home Page |  Forums |  Tracker |  Files | 

Recurrance

Welcome to Recurrance. This is a simple set of java classes that will interpret an RFC 2445 RRULE and give you the dates that the occurance would occur. The interface for building these recurrances is copied from the RFC and re-printed below.


As an example, if you needed to list everyday Wednsday and Friday in January, for 3 years:
    FREQ=YEARLY;BYMONTH=1;BYDAY=WE,FR
Recurrance would accept that string, and a starting date, and give you a list of all the dates you requested. More examples are listed below.


Installation Instructions


Confirmation

You can check your installation, with the built in recurrance.jsp. This jsp will let you enter an RRULE and will tell you the dates that recurrance returned.
Go to http://yourmachine:8080/recurrance to start using the software.


RELEASE NOTES:

For any questions or comments: jason@h3c.com


Infinite thanks to SourceForge for making a small project like this worth the effort of open sourcing.



SourceForge Logo

RFC 2445 RRULE Examples Reprint


/****

This is the RFC snippet used to develop this code.  It may change over time, so 
treat this as a NON-AUTHORITATIVE cache.

****/



4.3.10 Recurrence Rule

   Value Name: RECUR

   Purpose: This value type is used to identify properties that contain
   a recurrence rule specification.

   Formal Definition: The value type is defined by the following
   notation:

     recur      = "FREQ"=freq *(

                ; either UNTIL or COUNT may appear in a 'recur',
                ; but UNTIL and COUNT MUST NOT occur in the same 'recur'

                ( ";" "UNTIL" "=" enddate ) /
                ( ";" "COUNT" "=" 1*DIGIT ) /

                ; the rest of these keywords are optional,
                ; but MUST NOT occur more than once

                ( ";" "INTERVAL" "=" 1*DIGIT )          /
                ( ";" "BYSECOND" "=" byseclist )        /
                ( ";" "BYMINUTE" "=" byminlist )        /
                ( ";" "BYHOUR" "=" byhrlist )           /
                ( ";" "BYDAY" "=" bywdaylist )          /
                ( ";" "BYMONTHDAY" "=" bymodaylist )    /
                ( ";" "BYYEARDAY" "=" byyrdaylist )     /
                ( ";" "BYWEEKNO" "=" bywknolist )       /
                ( ";" "BYMONTH" "=" bymolist )          /
                ( ";" "BYSETPOS" "=" bysplist )         /
                ( ";" "WKST" "=" weekday )              /
                ( ";" x-name "=" text )
                )

     freq       = "SECONDLY" / "MINUTELY" / "HOURLY" / "DAILY"
                / "WEEKLY" / "MONTHLY" / "YEARLY"

     enddate    = date
     enddate    =/ date-time            ;An UTC value

     byseclist  = seconds / ( seconds *("," seconds) )

     seconds    = 1DIGIT / 2DIGIT       ;0 to 59

     byminlist  = minutes / ( minutes *("," minutes) )

     minutes    = 1DIGIT / 2DIGIT       ;0 to 59

     byhrlist   = hour / ( hour *("," hour) )

     hour       = 1DIGIT / 2DIGIT       ;0 to 23

     bywdaylist = weekdaynum / ( weekdaynum *("," weekdaynum) )

     weekdaynum = [([plus] ordwk / minus ordwk)] weekday

     plus       = "+"

     minus      = "-"

     ordwk      = 1DIGIT / 2DIGIT       ;1 to 53

     weekday    = "SU" / "MO" / "TU" / "WE" / "TH" / "FR" / "SA"
     ;Corresponding to SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
     ;FRIDAY, SATURDAY and SUNDAY days of the week.

     bymodaylist = monthdaynum / ( monthdaynum *("," monthdaynum) )

     monthdaynum = ([plus] ordmoday) / (minus ordmoday)

     ordmoday   = 1DIGIT / 2DIGIT       ;1 to 31

     byyrdaylist = yeardaynum / ( yeardaynum *("," yeardaynum) )

     yeardaynum = ([plus] ordyrday) / (minus ordyrday)

     ordyrday   = 1DIGIT / 2DIGIT / 3DIGIT      ;1 to 366

     bywknolist = weeknum / ( weeknum *("," weeknum) )

     weeknum    = ([plus] ordwk) / (minus ordwk)

     bymolist   = monthnum / ( monthnum *("," monthnum) )

     monthnum   = 1DIGIT / 2DIGIT       ;1 to 12

     bysplist   = setposday / ( setposday *("," setposday) )

     setposday  = yeardaynum

   Description: If the property permits, multiple "recur" values are
   specified by a COMMA character (US-ASCII decimal 44) separated list
   of values. The value type is a structured value consisting of a list
   of one or more recurrence grammar parts. Each rule part is defined by
   a NAME=VALUE pair. The rule parts are separated from each other by
   the SEMICOLON character (US-ASCII decimal 59). The rule parts are not
   ordered in any particular sequence. Individual rule parts MUST only
   be specified once.

   The FREQ rule part identifies the type of recurrence rule. This rule
   part MUST be specified in the recurrence rule. Valid values include
   SECONDLY, to specify repeating events based on an interval of a
   second or more; MINUTELY, to specify repeating events based on an
   interval of a minute or more; HOURLY, to specify repeating events
   based on an interval of an hour or more; DAILY, to specify repeating
   events based on an interval of a day or more; WEEKLY, to specify
   repeating events based on an interval of a week or more; MONTHLY, to
   specify repeating events based on an interval of a month or more; and
   YEARLY, to specify repeating events based on an interval of a year or
   more.

   The INTERVAL rule part contains a positive integer representing how
   often the recurrence rule repeats. The default value is "1", meaning
   every second for a SECONDLY rule, or every minute for a MINUTELY
   rule, every hour for an HOURLY rule, every day for a DAILY rule,
   every week for a WEEKLY rule, every month for a MONTHLY rule and
   every year for a YEARLY rule.

   The UNTIL rule part defines a date-time value which bounds the
   recurrence rule in an inclusive manner. If the value specified by
   UNTIL is synchronized with the specified recurrence, this date or
   date-time becomes the last instance of the recurrence. If specified
   as a date-time value, then it MUST be specified in an UTC time
   format. If not present, and the COUNT rule part is also not present,
   the RRULE is considered to repeat forever.

   The COUNT rule part defines the number of occurrences at which to
   range-bound the recurrence. The "DTSTART" property value, if
   specified, counts as the first occurrence.

   The BYSECOND rule part specifies a COMMA character (US-ASCII decimal
   44) separated list of seconds within a minute. Valid values are 0 to
   59. The BYMINUTE rule part specifies a COMMA character (US-ASCII
   decimal 44) separated list of minutes within an hour. Valid values
   are 0 to 59. The BYHOUR rule part specifies a COMMA character (US-
   ASCII decimal 44) separated list of hours of the day. Valid values
   are 0 to 23.

   The BYDAY rule part specifies a COMMA character (US-ASCII decimal 44)
   separated list of days of the week; MO indicates Monday; TU indicates
   Tuesday; WE indicates Wednesday; TH indicates Thursday; FR indicates
   Friday; SA indicates Saturday; SU indicates Sunday.

   Each BYDAY value can also be preceded by a positive (+n) or negative
   (-n) integer. If present, this indicates the nth occurrence of the
   specific day within the MONTHLY or YEARLY RRULE. For example, within
   a MONTHLY rule, +1MO (or simply 1MO) represents the first Monday
   within the month, whereas -1MO represents the last Monday of the
   month. If an integer modifier is not present, it means all days of
   this type within the specified frequency. For example, within a
   MONTHLY rule, MO represents all Mondays within the month.

   The BYMONTHDAY rule part specifies a COMMA character (ASCII decimal
   44) separated list of days of the month. Valid values are 1 to 31 or
   -31 to -1. For example, -10 represents the tenth to the last day of
   the month.

   The BYYEARDAY rule part specifies a COMMA character (US-ASCII decimal
   44) separated list of days of the year. Valid values are 1 to 366 or
   -366 to -1. For example, -1 represents the last day of the year
   (December 31st) and -306 represents the 306th to the last day of the
   year (March 1st).

   The BYWEEKNO rule part specifies a COMMA character (US-ASCII decimal
   44) separated list of ordinals specifying weeks of the year. Valid
   values are 1 to 53 or -53 to -1. This corresponds to weeks according
   to week numbering as defined in [ISO 8601]. A week is defined as a
   seven day period, starting on the day of the week defined to be the
   week start (see WKST). Week number one of the calendar year is the
   first week which contains at least four (4) days in that calendar
   year. This rule part is only valid for YEARLY rules. For example, 3
   represents the third week of the year.

        Note: Assuming a Monday week start, week 53 can only occur when
        Thursday is January 1 or if it is a leap year and Wednesday is
        January 1.


   The BYMONTH rule part specifies a COMMA character (US-ASCII decimal
   44) separated list of months of the year. Valid values are 1 to 12.

   The WKST rule part specifies the day on which the workweek starts.
   Valid values are MO, TU, WE, TH, FR, SA and SU. This is significant
   when a WEEKLY RRULE has an interval greater than 1, and a BYDAY rule
   part is specified. This is also significant when in a YEARLY RRULE
   when a BYWEEKNO rule part is specified. The default value is MO.

   The BYSETPOS rule part specifies a COMMA character (US-ASCII decimal
   44) separated list of values which corresponds to the nth occurrence
   within the set of events specified by the rule. Valid values are 1 to
   366 or -366 to -1. It MUST only be used in conjunction with another
   BYxxx rule part. For example "the last work day of the month" could
   be represented as:

     RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1

   Each BYSETPOS value can include a positive (+n) or negative (-n)
   integer. If present, this indicates the nth occurrence of the
   specific occurrence within the set of events specified by the rule.

   If BYxxx rule part values are found which are beyond the available
   scope (ie, BYMONTHDAY=30 in February), they are simply ignored.

   Information, not contained in the rule, necessary to determine the
   various recurrence instance start time and dates are derived from the
   Start Time (DTSTART) entry attribute. For example,
   "FREQ=YEARLY;BYMONTH=1" doesn't specify a specific day within the
   month or a time. This information would be the same as what is
   specified for DTSTART.

   BYxxx rule parts modify the recurrence in some manner. BYxxx rule
   parts for a period of time which is the same or greater than the
   frequency generally reduce or limit the number of occurrences of the
   recurrence generated. For example, "FREQ=DAILY;BYMONTH=1" reduces the
   number of recurrence instances from all days (if BYMONTH tag is not
   present) to all days in January. BYxxx rule parts for a period of
   time less than the frequency generally increase or expand the number
   of occurrences of the recurrence. For example,
   "FREQ=YEARLY;BYMONTH=1,2" increases the number of days within the
   yearly recurrence set from 1 (if BYMONTH tag is not present) to 2.

   If multiple BYxxx rule parts are specified, then after evaluating the
   specified FREQ and INTERVAL rule parts, the BYxxx rule parts are
   applied to the current set of evaluated occurrences in the following
   order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR,
   BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated.


   Here is an example of evaluating multiple BYxxx rule parts.

     DTSTART;TZID=US-Eastern: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". Then, lacking information from RRULE, the
   second is derived from DTSTART, to end up in "every Sunday in January
   at 8:30:00 AM and 9:30:00 AM, every other year". Similarly, if the
   BYMINUTE, BYHOUR, BYDAY, BYMONTHDAY or BYMONTH rule part were
   missing, the appropriate minute, hour, day or month would have been
   retrieved from the "DTSTART" property.

   No additional content value encoding (i.e., BACKSLASH character
   encoding) is defined for this value type.

   Example: The following is a rule which specifies 10 meetings which
   occur every other day:

     FREQ=DAILY;COUNT=10;INTERVAL=2

   There are other examples specified in the "RRULE" specification.


					
=========================================================================

4.8.5.4 Recurrence Rule

   Property Name: RRULE

   Purpose: This property defines a rule or repeating pattern for
   recurring events, to-dos, or time zone definitions.

   Value Type: RECUR

   Property Parameters: Non-standard property parameters can be
   specified on this property.

   Conformance: This property can be specified one or more times in
   recurring "VEVENT", "VTODO" and "VJOURNAL" calendar components. It
   can also be specified once in each STANDARD or DAYLIGHT sub-component
   of the "VTIMEZONE" calendar component.

   Description: The recurrence rule, if specified, is used in computing
   the recurrence set. The recurrence set is the complete set of
   recurrence instances for a calendar component. The recurrence set is
   generated by considering the initial "DTSTART" property along with
   the "RRULE", "RDATE", "EXDATE" and "EXRULE" properties contained
   within the iCalendar object. The "DTSTART" property defines the first
   instance in the recurrence set. Multiple instances of the "RRULE" and
   "EXRULE" properties can also be specified to define more
   sophisticated recurrence sets. The final recurrence set is generated
   by gathering all of the start date/times generated by any of the
   specified "RRULE" and "RDATE" properties, and excluding any start
   date/times which fall within the union of start date/times generated
   by any specified "EXRULE" and "EXDATE" properties. This implies that
   start date/times within exclusion related properties (i.e., "EXDATE"
   and "EXRULE") take precedence over those specified by inclusion
   properties (i.e., "RDATE" and "RRULE"). Where duplicate instances are
   generated by the "RRULE" and "RDATE" properties, only one recurrence
   is considered. Duplicate instances are ignored.


   The "DTSTART" and "DTEND" property pair or "DTSTART" and "DURATION"
   property pair, specified within the iCalendar object defines the
   first instance of the recurrence. When used with a recurrence rule,
   the "DTSTART" and "DTEND" properties MUST be specified in local time
   and the appropriate set of "VTIMEZONE" calendar components MUST be
   included. For detail on the usage of the "VTIMEZONE" calendar
   component, see the "VTIMEZONE" calendar component definition.

   Any duration associated with the iCalendar object applies to all
   members of the generated recurrence set. Any modified duration for
   specific recurrences MUST be explicitly specified using the "RDATE"
   property.

   Format Definition: This property is defined by the following
   notation:

     rrule      = "RRULE" rrulparam ":" recur CRLF

     rrulparam  = *(";" xparam)

   Example: All examples assume the Eastern United States time zone.

   Daily for 10 occurrences:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=DAILY;COUNT=10

     ==> (1997 9:00 AM EDT)September 2-11

   Daily until December 24, 1997:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=DAILY;UNTIL=19971224T000000Z

     ==> (1997 9:00 AM EDT)September 2-30;October 1-25
         (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23

   Every other day - forever:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=DAILY;INTERVAL=2
     ==> (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;
          October 2,4,6...20,22,24
         (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;
          Dec 1,3,...

   Every 10 days, 5 occurrences:



     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5

     ==> (1997 9:00 AM EDT)September 2,12,22;October 2,12

   Everyday in January, for 3 years:

     DTSTART;TZID=US-Eastern:19980101T090000
     RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;
      BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
     or
     RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1

     ==> (1998 9:00 AM EDT)January 1-31
         (1999 9:00 AM EDT)January 1-31
         (2000 9:00 AM EDT)January 1-31

   Weekly for 10 occurrences

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=WEEKLY;COUNT=10

     ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
         (1997 9:00 AM EST)October 28;November 4

   Weekly until December 24, 1997

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z

     ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
         (1997 9:00 AM EST)October 28;November 4,11,18,25;
                           December 2,9,16,23
   Every other week - forever:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU

     ==> (1997 9:00 AM EDT)September 2,16,30;October 14
         (1997 9:00 AM EST)October 28;November 11,25;December 9,23
         (1998 9:00 AM EST)January 6,20;February
     ...

   Weekly on Tuesday and Thursday for 5 weeks:

    DTSTART;TZID=US-Eastern:19970902T090000
    RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
    or
    RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH

    ==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2

   Every other week on Monday, Wednesday and Friday until December 24,
   1997, but starting on Tuesday, September 2, 1997:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;
      BYDAY=MO,WE,FR
     ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October
     1,3,13,15,17
         (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;
                           December 8,10,12,22

   Every other week on Tuesday and Thursday, for 8 occurrences:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH

     ==> (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16

   Monthly on the 1st Friday for ten occurrences:

     DTSTART;TZID=US-Eastern:19970905T090000
     RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR

     ==> (1997 9:00 AM EDT)September 5;October 3
         (1997 9:00 AM EST)November 7;Dec 5
         (1998 9:00 AM EST)January 2;February 6;March 6;April 3
         (1998 9:00 AM EDT)May 1;June 5

   Monthly on the 1st Friday until December 24, 1997:

     DTSTART;TZID=US-Eastern:19970905T090000
     RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR

     ==> (1997 9:00 AM EDT)September 5;October 3
         (1997 9:00 AM EST)November 7;December 5

   Every other month on the 1st and last Sunday of the month for 10
   occurrences:

     DTSTART;TZID=US-Eastern:19970907T090000
     RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU

     ==> (1997 9:00 AM EDT)September 7,28
         (1997 9:00 AM EST)November 2,30
         (1998 9:00 AM EST)January 4,25;March 1,29
         (1998 9:00 AM EDT)May 3,31

   Monthly on the second to last Monday of the month for 6 months:

     DTSTART;TZID=US-Eastern:19970922T090000
     RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO

     ==> (1997 9:00 AM EDT)September 22;October 20
         (1997 9:00 AM EST)November 17;December 22
         (1998 9:00 AM EST)January 19;February 16

   Monthly on the third to the last day of the month, forever:

     DTSTART;TZID=US-Eastern:19970928T090000
     RRULE:FREQ=MONTHLY;BYMONTHDAY=-3

     ==> (1997 9:00 AM EDT)September 28
         (1997 9:00 AM EST)October 29;November 28;December 29
         (1998 9:00 AM EST)January 29;February 26
     ...

   Monthly on the 2nd and 15th of the month for 10 occurrences:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15

     ==> (1997 9:00 AM EDT)September 2,15;October 2,15
         (1997 9:00 AM EST)November 2,15;December 2,15
         (1998 9:00 AM EST)January 2,15

   Monthly on the first and last day of the month for 10 occurrences:

     DTSTART;TZID=US-Eastern:19970930T090000
     RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1

     ==> (1997 9:00 AM EDT)September 30;October 1
         (1997 9:00 AM EST)October 31;November 1,30;December 1,31
         (1998 9:00 AM EST)January 1,31;February 1

   Every 18 months on the 10th thru 15th of the month for 10
   occurrences:

     DTSTART;TZID=US-Eastern:19970910T090000
     RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,
      15

     ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15
         (1999 9:00 AM EST)March 10,11,12,13

   Every Tuesday, every other month:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU

     ==> (1997 9:00 AM EDT)September 2,9,16,23,30
         (1997 9:00 AM EST)November 4,11,18,25
         (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
     ...

   Yearly in June and July for 10 occurrences:

     DTSTART;TZID=US-Eastern:19970610T090000
     RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
     ==> (1997 9:00 AM EDT)June 10;July 10
         (1998 9:00 AM EDT)June 10;July 10
         (1999 9:00 AM EDT)June 10;July 10
         (2000 9:00 AM EDT)June 10;July 10
         (2001 9:00 AM EDT)June 10;July 10
     Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components
     are specified, the day is gotten from DTSTART

   Every other year on January, February, and March for 10 occurrences:

     DTSTART;TZID=US-Eastern:19970310T090000
     RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3

     ==> (1997 9:00 AM EST)March 10
         (1999 9:00 AM EST)January 10;February 10;March 10
         (2001 9:00 AM EST)January 10;February 10;March 10
         (2003 9:00 AM EST)January 10;February 10;March 10

   Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:

     DTSTART;TZID=US-Eastern:19970101T090000
     RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200

     ==> (1997 9:00 AM EST)January 1
         (1997 9:00 AM EDT)April 10;July 19
         (2000 9:00 AM EST)January 1
         (2000 9:00 AM EDT)April 9;July 18
         (2003 9:00 AM EST)January 1
         (2003 9:00 AM EDT)April 10;July 19
         (2006 9:00 AM EST)January 1

   Every 20th Monday of the year, forever:
     DTSTART;TZID=US-Eastern:19970519T090000
     RRULE:FREQ=YEARLY;BYDAY=20MO

     ==> (1997 9:00 AM EDT)May 19
         (1998 9:00 AM EDT)May 18
         (1999 9:00 AM EDT)May 17
     ...

   Monday of week number 20 (where the default start of the week is
   Monday), forever:

     DTSTART;TZID=US-Eastern:19970512T090000
     RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO

     ==> (1997 9:00 AM EDT)May 12
         (1998 9:00 AM EDT)May 11
         (1999 9:00 AM EDT)May 17
     ...

   Every Thursday in March, forever:

     DTSTART;TZID=US-Eastern:19970313T090000
     RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH

     ==> (1997 9:00 AM EST)March 13,20,27
         (1998 9:00 AM EST)March 5,12,19,26
         (1999 9:00 AM EST)March 4,11,18,25
     ...

   Every Thursday, but only during June, July, and August, forever:

     DTSTART;TZID=US-Eastern:19970605T090000
     RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8

     ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;
                       August 7,14,21,28
         (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;
                       August 6,13,20,27
         (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;
                       August 5,12,19,26
     ...

   Every Friday the 13th, forever:

     DTSTART;TZID=US-Eastern:19970902T090000
     EXDATE;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13




     ==> (1998 9:00 AM EST)February 13;March 13;November 13
         (1999 9:00 AM EDT)August 13
         (2000 9:00 AM EDT)October 13
     ...

   The first Saturday that follows the first Sunday of the month,
    forever:

     DTSTART;TZID=US-Eastern:19970913T090000
     RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13

     ==> (1997 9:00 AM EDT)September 13;October 11
         (1997 9:00 AM EST)November 8;December 13
         (1998 9:00 AM EST)January 10;February 7;March 7
         (1998 9:00 AM EDT)April 11;May 9;June 13...
     ...

   Every four years, the first Tuesday after a Monday in November,
   forever (U.S. Presidential Election day):

     DTSTART;TZID=US-Eastern:19961105T090000
     RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8

     ==> (1996 9:00 AM EST)November 5
         (2000 9:00 AM EST)November 7
         (2004 9:00 AM EST)November 2
     ...

   The 3rd instance into the month of one of Tuesday, Wednesday or
   Thursday, for the next 3 months:

     DTSTART;TZID=US-Eastern:19970904T090000
     RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3

     ==> (1997 9:00 AM EDT)September 4;October 7
         (1997 9:00 AM EST)November 6

   The 2nd to last weekday of the month:

     DTSTART;TZID=US-Eastern:19970929T090000
     RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2

     ==> (1997 9:00 AM EDT)September 29
         (1997 9:00 AM EST)October 30;November 27;December 30
         (1998 9:00 AM EST)January 29;February 26;March 30
     ...


   Every 3 hours from 9:00 AM to 5:00 PM on a specific day:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z

     ==> (September 2, 1997 EDT)09:00,12:00,15:00

   Every 15 minutes for 6 occurrences:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6

     ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15

   Every hour and a half for 4 occurrences:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4

     ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30

   Every 20 minutes from 9:00 AM to 4:40 PM every day:

     DTSTART;TZID=US-Eastern:19970902T090000
     RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40
     or
     RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40

     ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
                                ... 16:00,16:20,16:40
         (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
                               ...16:00,16:20,16:40
     ...

   An example where the days generated makes a difference because of
   WKST:

     DTSTART;TZID=US-Eastern:19970805T090000
     RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO

     ==> (1997 EDT)Aug 5,10,19,24

     changing only WKST from MO to SU, yields different results...

     DTSTART;TZID=US-Eastern:19970805T090000
     RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
     ==> (1997 EDT)August 5,17,19,31



=========================================================================

Personally evil testing / Things to think about if you're simplifying this code:

Can it handle:
  - Every seven months vs The seventh month of every year? ( 7 is the trick, not 6,3,2 or 1 )
  - Can it handle BYxyz rules both greater and less than the FREQ value?