Subject: [Ietf-calsify] BYSETPOS and contracting BY* rules

From: lennox at cs.columbia.edu (Jonathan Lennox)
Date: Mon Nov 6 19:14:29 2006

In a hallway discussion today, I happened to remember another ambiguity in
the iCalendar spec, which was brought up back on the old ietf-calendar
mailing list but not, I think, ever resolved.

The issue is that BYSETPOS is specified only as

    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. 

and the meaning of "the set of events specified by the rule" is very
unclear.

Particularly, the case that none of the RFC 2445 examples illustrates is
what happens when BYSETPOS is used in conjunction with contracting BY*
rules, i.e. BY* rules describing intervals larger than the FREQ.

The examples I came up with back in June 2001 (see the thread starting at
<http://www.imc.org/ietf-calendar/mail-archive/thrd11.html#01781> if you
really care about all the gory details) were:

       RRULE:DTSTART;TZID=America/New_York:20010401T090000
       RECUR:FREQ=WEEKLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1

       RRULE:DTSTART;TZID=America/New_York:20010401T090000
       RECUR:FREQ=MONTHLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1

       RRULE:DTSTART;TZID=America/New_York:20010401T090000
       RECUR:FREQ=YEARLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1

My proposal at the time was that these mean, respectively, "the last Friday
of the week, in April and June" (i.e. "Fridays in April and June", with
BYSETPOS irrelevant); "the last Friday of April, and the last Friday of
June"; and "the last Friday of (April or June)" (i.e., given DTSTART, "the
last Friday of June").

I still think this is right, but at the time not everyone agreed.

(Note that these examples don't obey the new "DTSTART must be a set member"
constraint.  It's an interesting question how to obey that for rules that
contain "BYSETPOS=-1".)

Eric Busboom's 2445 revision draft at the time defined this by saying

       BYSETPOS operates on a set of events in one interval of the
       recurrence rule. For a WEEKLY rule, the interval is one week, for a
       MONTHLY rule, one month, and for a YEARLY rule, one year.

This wording seems not to have made it into the current 2445bis draft.

How do existing CUAs (if they implement BYSETPOS at all) interpret my three
examples above?  Do people agree with my suggested interpretation?

-- 
Jonathan Lennox
[email-masked]

---

From: reinhold at kainhofer.com (Reinhold Kainhofer)
Date: Tue Nov 7 02:24:58 2006

Am Dienstag, 7. November 2006 04:13 schrieb Jonathan Lennox:
> ? ? ? ?RRULE:DTSTART;TZID=America/New_York:20010401T090000
> ? ? ? ?RECUR:FREQ=WEEKLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1
>
> ? ? ? ?RRULE:DTSTART;TZID=America/New_York:20010401T090000
> ? ? ? ?RECUR:FREQ=MONTHLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1
>
> ? ? ? ?RRULE:DTSTART;TZID=America/New_York:20010401T090000
> ? ? ? ?RECUR:FREQ=YEARLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1
>

I think so, too.

A similar issue happens with BYDAY, which is similar to BYSETPOS, only that it 
is evaluated before all BY(HOUR|MINUTE|SECOND) are evaluated and that it has 
an additional constraint of giving one or more weekdays, too.

        DTSTART;TZID=America/New_York:20010401T090000
        RRULE:FREQ=WEEKLY;BYMONTH=4,6;BYDAY=-1FR

        DTSTART;TZID=America/New_York:20010401T090000
        RRULE:FREQ=MONTHLY;BYMONTH=4,6;BYDAY=-1FR

        DTSTART;TZID=America/New_York:20010401T090000
        RRULE:FREQ=YEARLY;BYMONTH=4,6;BYDAY=-1FR

AFAIU RFC 2445, the -1FR means the last friday in the set of dates that match 
the rule evaluation so far. Thus, I get the same results as with BYSETPOS:

In the first example, where we loop over each week, that set contains only one 
Mon-Sun of that week, so the -1 is irrelevant.
In the second example, we loop over all months, the bymonth returns an empty 
set for all months other than april and jung. But in april and in june, the 
set contains all days of that month, so -1FR is the last friday of april when 
the loop is in april, and the last friday of june, when the loop reache june.
In the third example, we loop over all years, then choose only all days from 
april and june, and finally the BYDAY selects the last friday of that set, 
which is the last friday of june.

It's even more interesting when we add BYWEEKNO...

Or am I misunderstanding the BYDAY rule part here, and 
        RRULE:FREQ=YEARLY;BYWEEKNO=3;BYDAY=-1FR
matches only the last friday of the year if it is in week #3?
If so, why is the order of the evaluation relevant? The only thing that is 
important in that case is that BYSETPOS are evaluated last, all other BY* 
rule parts else can be evaluated in any order...

That's another example that would help a lot if it appeared in an official 
FAQ. As I already mentioned several times, the RFC explains only some very 
simple RRULEs, which are quite clear anyway, but forgets to give examples 
where the details are important (e.g. in the examples given, the order of the 
evaluation of the rule parts is irrelevant, while in reality there are 
several cases, where you get a different result if you evaluate the parts in 
a different order).

Cheers,
Reinhol
  
-- 
------------------------------------------------------------------
Reinhold Kainhofer, Vienna University of Technology, Austria
email: [email-masked], http://reinhold.kainhofer.com/
 * Financial and Actuarial Mathematics, TU Wien, http://www.fam.tuwien.ac.at/
 * K Desktop Environment, http://www.kde.org, KOrganizer maintainer
 * Chorvereinigung "Jung-Wien", http://www.jung-wien.at/

---

From: lennox at cs.columbia.edu (Jonathan Lennox)
Date: Tue Nov 7 06:55:15 2006

> the rule evaluation so far. Thus, I get the same results as with BYSETPOS:

I've always agreed with your second interpretation of RFC 2445: the numbers
in the BYDAY rule refer to the period of time described by the FREQ.  "-1FR"
means the last Friday of the month in a MONTHLY rule, and the last Friday of
the year in a YEARLY rule.

This seems to me to be the most straightforward reading of the BYDAY
definition:

      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.

That would indeed mean that the other BY* rules in the rule aren't relevant
to the interpretation of BYDAY, and that the order of interpretation of BY*
rules other than BYSETPOS isn't important.

Unfortunately, this interpretation is directly contradicted by RFC 2445's
VTIMEZONE example, which describes US-Eastern DST transitions as

        RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10

I've always interpreted this VTIMEZONE example as a bug in the spec, but I
see how it can make sense if BYDAY integers are interpreted as being like
BYSETPOS.

What do other implementations do?

-- 
Jonathan Lennox
[email-masked]

---

From: mikesamuel at gmail.com (Mike Samuel)
Date: Tue Nov 7 20:56:07 2006

> What do other implementations do?

Google Calendar treats this as the last Sunday of the year, if it
happens to fall in October, which it never can, so this recurrence
will generate no results.

---

From: mikesamuel at gmail.com (Mike Samuel)
Date: Tue Nov 7 21:02:00 2006

> examples above?  Do people agree with my suggested interpretation?

Google Calendar will generate each Friday in April and June for
FREQ=WEEKLY;BYMONTH=4,6;BYDAY=FR;BYSETPOS=-1

It applies the BYSETPOS to each period, so each week independently in
this case..  Since the set of Fridays in a given week has cardinality
1, the BYSETPOS has no effect.