Ref T10747. This isn't done and literally took me all day, but the basics are kind of working and I think I mostly just have to fill in a few more things and then heap piles and piles of tests on it.
Here's an RRULE that is difficult to evaluate quickly:
$rrule = id(new PhutilCalendarRecurrenceRule()) ->setStartDateTime($start) ->setFrequency('SECONDLY') ->setByMonth(array(1)) ->setByMonthDay(array(1)) ->setByHour(array(12)) ->setByMinute(array(0)) ->setBySecond(array(0));
Here's how long it takes to evaluate on various RRULE libraries:
PhutilCalendarRecurrenceRule | Instantaneous |
php-rrule | 11 seconds |
rrule.js | Completely Freezes |
python-dateutil | 7 seconds |
recurr (PHP) | Didn't actually test it, but looks like the same code |
Apparently someone wrote python-dateutil, and then people have just been porting that to other languages ever since. python-dateutil is not very fast at evaluating these kinds of rules, and all of the ports seem worse.
In the case of recurr, it looks like someone ported python-dateutil to JS (rrule.js), then ported the JS to PHP.
The structure of all these libraries appears essentially the same, and they pretty much brute force their way through evaluating rules like this.
It isn't really important to be able to evaluate this rule quickly, and I haven't finished this implementation so may approach may actually be garbage that I have to throw out, but being dramatically better in one contrived edge case makes me feel like I accomplished something more than gaining a deeper personal understanding of RRULEs by implementing this.