dmfs / lib-recur

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

Too many filtered recurrent instances when creating iterator. #100

Closed GeekNickson closed 3 years ago

GeekNickson commented 3 years ago

To give some context, I'm trying to generate occurences of the following RRULE: FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=31.

My code goes as follows: RecurrenceRuleIterator iterator = rrule.iterator(startDate.toInstant().toEpochMilli(), TimeZone.getTimeZone(startDate.getZone()));

startDate is a ZonedDateTime object, more specifically 2021-07-01T00:00+03:00

Basically, it doesn't let me to create an iterator because it throws an IllegalArgumentException with the message "too many filtered recurrence instances" in SanityFilter.java on line 89 inside next() method. I'm at loss, I have no idea why it generates so many instances upon creating an iterator and how to tell it to stop.

I've also tried to set until property to rrule but that didn't help either:(

dmfs commented 3 years ago

The problem is that the rule FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=31 doesn't generate any instances at all because it would only iterate February 31st, which is not a real date. RFC 5545 says non-existent dates should be filtered from the resulting recurrence set.

That's exactly what SanityFilter does, it removes any invalid results. The exception you get is essentially a protection against an infinite loop.

dmfs commented 3 years ago

Forgot to add: The iterator tries to find the first instance when it's instantiated. That's why you already get the exception when you create it. Nowadays I'd call that a bad design, but I didn't know any better back then when I wrote this.

Also, adding an until doesn't help, because it's applied after the sanity filter. It's probably a good idea to apply the sanity filter after until. I have to check the side effects that might be caused by a change like this.

dmfs commented 3 years ago

I just checked, applying the sanity filter after UNTIL is unfortunately not trivial because it has to be applied before BYSETPOS is evaluated but BYSETPOS has to be evaluated before UNTIL is evaluated. So this requires some special treatment.

GeekNickson commented 3 years ago

The thing was that I'd been trying to create an event on the 31'nd of February, which obviously doesn't exist:)

GeekNickson commented 3 years ago

@dmfs I also wanted to ask if there's any way I can get the count of event instances it will generate without manually iterating. I know about Guava's Iterators.size(), but it iterates under the hood anyway.

dmfs commented 3 years ago

unfortunately, no. This would only work for very simple rules (or rules that have a COUNT part ;-) ). Many rules are too complex to determine the number of instances without iterating them all, not to mention infinite rules.

GeekNickson commented 3 years ago

Thanks for the answer though!