Subject: INTERVAL, DTSTART and BYxxx modifiers

From: "Laird Nelson" <[email-masked]>
Date: Tue, 7 Jun 2005 11:28:07 -0400

I hope this is a meaningful question; please point me in the right direction
if not.

Consider the following arbitrary RRULE, significant mainly for its INTERVAL
rule part:

  DTSTART;TZID=US-Eastern:19970105T083000
  RRULE:FREQ=DAILY;INTERVAL=5;BYMONTH=1

Now suppose that I want my recurrence engine to return me the next instance
relative to today's date (June 7, 2005).

If the INTERVAL were 1 (or, equivalently, not specified), then it would be
clear that the next instance would be January 1, 2006 08:30:00 EST.

Would the same be true when the INTERVAL is 5 (as it is in my example)?  Or
must I first compute the total set of *unmodified* occurrences (e.g. every
fifth day, period, end of story) and then eliminate all such instances that
are not "contained" by the BYMONTH modifier?  Another way to tput this is,
sitting here, I'm not sure offhand what day in January 2006 you'll "land on"
if you iterate over every fifth day, starting from January 1, 1997, so I'm
not sure offhand which day in January 2006 matches the rule.  Must I perform
this calculation before applying my BYxxx rule parts?

RFC 2445 says:

   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 leads me to believe that I must actually compute the entire set of
occurrences (when INTERVAL > 1) even if all I want to do is just find the
occurrence immediately after some reference date.  Is that the intention?

Thanks,
Laird

---

From: Reinhold Kainhofer <[email-masked]>
Date: Tue, 7 Jun 2005 18:12:23 +0200

>   RRULE:FREQ=DAILY;INTERVAL=5;BYMONTH=1
[...]
> are not "contained" by the BYMONTH modifier? Another way to put this is, 
> sitting here, I'm not sure offhand what day in January 2006 you'll "land
> on" if you iterate over every fifth day, starting from January 1, 1997, so
> I'm not sure offhand which day in January 2006 matches the rule.  Must I
> perform this calculation before applying my BYxxx rule parts?

Yes, exactly.

Actually, there's no need to actually step through all 5-day steps sine Jan 5 
1997. Just calculate the days that have elapsed since Jan 5 1997, and get the 
next multiple of the frequency. That's the next valid interval on or after 
the date. This works just fine for seconds, minutes, hours, days, weeks, 
months and years.

E.g. Days from Jan 5, 1997, until June 7, 2005: 3075
So, June 7, 2005, is a valid recurrence interval. Check all BY* parts to see 
if there are any occurences in this interval. Since there are not, increase 
the date by 5 days.

For June 9, you'll have 3077 days since DTSTART, so you'll have to add 3 days 
to get to a multiple of 5. Check all BY* to see if there's a recurrence on 
June 12. If not, add 5 days and start over with checking the BY*. Do this, 
until you have an occurence, or you are outside a date/time range that is of 
interest (or the loop count exceeds a certain threshold so you can assume the 
rule is contradictory, but you haven't detected this before).

Of course, you might think of optimizing this by analyzing the BY* parts, and 
seeing that only occurences in Jan are allowed, so you can skip all months 
until Jan 1, 2006. This is 3283 days after the DTSTART, so Jan 3, 2006 would 
be the next allowed interval. Again, check all BY* parts, etc... 
But beware of correlations between the BY* parts when trying to optimize 
things! 

Reinhold

-- 
------------------------------------------------------------------
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 / KPilot maintainer