dmfs / lib-recur

A recurrence processor for Java
Apache License 2.0
199 stars 47 forks source link

Too many empty recurrence sets exception thrown when creating a recurrence iterator from a valid recurrence rule #108

Closed baparson closed 1 year ago

baparson commented 1 year ago

The recurrence rule looks like this:

FREQ=YEARLY;INTERVAL=1;BYMONTHDAY=20;BYDAY=MO;BYSETPOS=3

So, creating a rule from that and attempt to get the iterator from that rule throws the following exception: IllegalStateException("too many empty recurrence sets") from within the final class BySetPosFilter class.

Yeah, this is quite the edge case here for a recurrence, but I'm wondering if there's a way around this using this library, or is this case simply not supported by the current latest version of this library?

dmfs commented 1 year ago

So to put this in words, the rule should match the third Monday that falls on a 20th every year? How often does that rule match? From a quick glance I'd say not too often, maybe. This should only fail if there are 1000 consecutive years that have less than 3 such Mondays. This year has three such Mondays, so I guess it does happen every now and then. So this is a bug.

dmfs commented 1 year ago

Have you checked the results when you yet BYSETPOS=2? Does it return correct results then?

baparson commented 1 year ago

Yeah, the schedule is "the third Monday the 20th of the year", which actually happens every 11 years. It's a very odd schedule.

baparson commented 1 year ago

Have you checked the results when you yet BYSETPOS=2? Does it return correct results then?

I can see that happens a little more frequently on the timeline, but I still get the same error with BYSETPOS=2.

baparson commented 1 year ago

For the record, I'm currently using version 0.11.4. I tried upgrading to the latest version, but saw the same error.

dmfs commented 1 year ago

thx, I'll have a closer look into the code and fix the issue.

baparson commented 1 year ago

thx, I'll have a closer look into the code and fix the issue.

Excellent! Thanks! 👍

dmfs commented 1 year ago

So, upon taking a closer look, I'm not sure this is a bug anymore. I think it actually works as intended. RFC 5545 states on page 43:

 Information, not contained in the rule, necessary to determine the
 various recurrence instance start time and dates are derived from
 the Start Time ("DTSTART") component 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".

In your case the month is not specified in the rule, so it is inherited from the start date. That means expansion takes place only for the month of your start date, which makes it impossible to generate a 3rd "Monday 20th" (because a month can have at most one of them).

Regarding BYDAY in a YEARLY rule, it clearly states

Limit if BYYEARDAY or BYMONTHDAY is present

Which is the case.

Based on that I would conclude that the expansion is executed correctly. As a "workaround" you can change your rule to this:

FREQ=YEARLY;BYMONTH=1,2,3,4,5,6,7,8,9,10,11,12;INTERVAL=1;BYMONTHDAY=20;BYDAY=MO;BYSETPOS=3

Inserting the BYMONTH part ensures that the 20th of each month is expanded before retaining only the 3rd one that falls on a Monday.

Have you checked how other calendar applications interpret this rule?

dmfs commented 1 year ago

I'm closing this as "works as intended". If you have doubts, please let me know.

baparson commented 1 year ago

Thanks for this explanation! This gives me something to go by in coming up with a workaround.