Subject: BYDAY and YEARLY

From: "Dan Hickman" <[email-masked]>
Date: Mon, 7 Jul 2003 14:16:10 -0500

The correct usage of BYDAY in a yearly RRULE has been discussed several
times on the working group before but I have not seen a resolution about the
correct meaning.  I would like to put it to rest once and for all.

The issue is the use of BYDAY as follows to mean the last Sunday in October.
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10

The BYDAY on yearly refers to weeks of the year and not weeks of the month
so this really means the last Sunday of the year but only if it occurs in
October.  The last week of the year will always be in December of course so
the above rule never resolves to any valid dates.

The following examples would be correct:
RRULE:FREQ=MONTHLY;BYDAY=-1SU;BYMONTH=10
or
RRULE:FREQ=YEARLY;BYDAY=SU;BYMONTH=10;BYSETPOS=-1

The problem is that RFC2445 incorrectly uses this syntax in VTIMEZONE
examples so it makes it look like it is valid.  I am finding now that both
MS Outlook and Apple iCal both use YEARLY/BYDAY in this way.

It was suggested on this mailing list in the past that the VTIMEZONE text
should be corrected in the RFC.  I really think this should be addressed,
one way or another.

If the opinion is that "RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10" does mean
the last Sunday in October, then that raises further questions.  For
example, what dates would the following rule resolve to?
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10,11,12

Regards,
Dan Hickman
Rockin' Software
[email-masked]
http://www.rockinsoftware.com

---

From: [email-masked]
Date: Tue, 8 Jul 2003 10:16:09 -0400

> the above rule never resolves to any valid dates.

I think a couple points from 2445 may help here (all from Section 4.3.10 
Recurrence Rule).  The first tidbit:

   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 second tidbit is:

   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.

This second tidbit describes the case in Dans example rule; the BYDAY and 
BYMONTH modify the recurrence by expanding the set.  However since the 
BYMONTH and BYDAY parts only have 1 value each the set is not actually 
expanded but more modified.

By applying the first tidbit alone to just RRULE:FREQ=YEARLY;BYDAY=-1SU 
alone I would get the last Sunday in the year (the date is calculatable, 
the time is inherited from DTSTART).  However as Ki noted (from the second 
tidbit) there are other modifiers in play here and that is BYMONTH (or 
BYDAY if you follow the expressed order in the last paragraph cited).

Thus evaluating the rule:

You start with DTSTART and apply the FREQ and INTERVAL to get "Repeats 
yearly on the DTSTART date/time".

Then you apply the BYxxx rule parts in the order proscribed.  The first 
would be BYMONTH resulting in "Repeats yearly on the DTSTART date/time in 
the month of October" (the day and time are inherited, the month is 
overridden by BYMONTH).  The next step would be to apply the next BYxxx 
rule part we have, BYDAY.  This would result in "Repeats yearly on the 
Last Sunday (at the DTSTART time) in the month of October".

So the final set of instances would be DTSTART and on the last Sunday in 
October that immediately follows the DTSTART (it could be the next year or 
the same year depending on when DTSTART was).  At least thats how I take 
the text from the RFCs on recurrence rule evaluation.

In rechecking Dans text I see that he said "The BYDAY on yearly refers to 
weeks of the year and not weeks of the month" but BYDAY is defined as:

   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.

which is not quite the same.  BYWEEK would be "weeks of the year".  BYDAY 
is defined as "days of the week".

> RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10,11,12

With a BYMONTH=10,11,12 we would follow the same evaluation logic above 
and get 3 instances (the last Sundays of October, November and December) 
and that exactly jives w/the bit about "expand the number of occurences of 
the recurrence" from 2445.

The same expansion logic can be confirmed by using more than 1 value for 
the BYDAY as well. 

Bruce
===========================================================================
Bruce Kahn                                INet: 
[email-masked]
Messaging & Collaboration                 Phone: 978.399.6496
IBM Software Group                         FAX: and nothing but the FAX...
Standard disclaimers apply, even where prohibited by law...

---

From: Jonathan Lennox <[email-masked]>
Date: Tue, 8 Jul 2003 11:35:11 -0400

> Last Sunday (at the DTSTART time) in the month of October".

I agree with this so far, but in your next step (concluding that this means
"the last Sunday in October") you make an unwarranted leap.  The definition
of the integer parts of BYDAY says, as you quoted previously:

>    specific day within the MONTHLY or YEARLY RRULE. 

Thus, since this is a YEARLY RRULE, the phrase "Last Sunday" means "Last
Sunday of the Year", not "Last Sunday of the Month."  The presence of a
BYMONTH rule doesn't affect this; by the spec, the integer part of BYDAY
depends *only* on the FREQ.

There is, of course, no last Sunday of the Year in October.  But this just
means that the quoted rule is vacuous, and its recurrence defines the empty
set, except for its DTSTART.  There's nothing "wrong" with that, any more
than there's something "wrong" with
     RRULE:FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=30

-- 
Jonathan Lennox
lennox at cs dot columbia dot edu

---

From: [email-masked]
Date: Tue, 8 Jul 2003 15:06:31 -0400

> depends *only* on the FREQ.

The assumption about BYDAY would be accurate without other BYxxx modifiers 
present.  When I read:

   order:

I take the phrase "applied to the current set of evaluated occurrences" to 
be that BYDAY works on the modified occurrences generated by applying the 
BYMONTH first.  This BYxxx modifier scopes the instance set to the month 
of October rather than the entire year. 

If BYMONTH were multivalued (ie: BYMONTH=10,11,12) then the occurrences 
would be scoped to the months of October thru December.  If no other BYxxx 
rule parts were there then Id say that the rule would evaluate to "The 
last Sunday in December" but because other BYxxx modfiiers were involved 
this is not true.

On the top of page 45 in 2445 there is an example of this that is pretty 
similar to Dans rule:

   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".
...

This evaluates just like I did for the BYDAY=-1SU;BYMONTH=10.  It follows 
the same logic I used (or I should say that my logic followed it w/o 
actually seeing this @ first) by applying the cited bits of 2445.

>                                                             But this 
just
> set, except for its DTSTART. 

I do not think so given that the evaluation of the rule matches the way 
that the multiple BYxxx parts are described to modify each other (and are 
not applyed in parallel) and that the example in 2445 seems to confirm 
this.

Bruce

---

From: "Dan Hickman" <[email-masked]>
Date: Tue, 8 Jul 2003 19:37:42 -0500

The one thing that is obvious given the repeated conversations on this topic
is that the specification of BYDAY on YEARLY is ambiguous in the RFC. I see
both sides as valid interpretations. I suppose the real question is what was
the original intention of the author(s)?

Given the following:
1. Numerous VTIMEZONE examples in the RFC use
"RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10" to mean "The last Sunday in
October", making it a big statement to say that these are not valid.
2. There are a number of implementations that already work this way now.
3. Rules like "The 5th Monday of the year but only in February" are probably
not that useful.

it appears there is much less impact if we go ahead and consider
"FREQ=YEARLY;BYMONTH=2;BYDAY=5MO" to mean the "5th Monday in February".  I
suggest we accept this interpretation.

Too bad I picked the wrong interpretation to implement :-|

Regards,
Dan Hickman
Rockin' Software

  -----Original Message-----
  From: [email-masked]
[mailto:[email-masked]]On Behalf Of
[email-masked]
  Sent: Tuesday, July 08, 2003 2:07 PM
  To: Jonathan Lennox
  Cc: [email-masked]
  Subject: Re: BYDAY and YEARLY

  I take the phrase "applied to the current set of evaluated occurrences" to
be that BYDAY works on the modified occurrences generated by applying the
BYMONTH first.  This BYxxx modifier scopes the instance set to the month of
October rather than the entire year.

  If BYMONTH were multivalued (ie: BYMONTH=10,11,12) then the occurrences
would be scoped to the months of October thru December.  If no other BYxxx
rule parts were there then Id say that the rule would evaluate to "The last
Sunday in December" but because other BYxxx modfiiers were involved this is
not true.

just

  I do not think so given that the evaluation of the rule matches the way
that the multiple BYxxx parts are described to modify each other (and are
not applyed in parallel) and that the example in 2445 seems to confirm this.

  Bruce