0

How to generate an RRULE (any RRULE, no matter how supoptimal) from a series of dates— or, failing that, to use RRULE-defined dates in an easily compatible way with lists of dates.

In other words, how to deal with arbitrary lists of dates that modern computer science has collectively decided are impossible to convert to RRULEs, while simultaneously supporting the approach that everyone says to take, RRULE.

Yet deriving RRULEs from lists of dates is so obviously a job for computers that i find it hard to believe that it's not a thing you have to do in third year of undergraduate computer science.

Yes, i've seen this answer: "it's impossible to know the exact rule that generated a collection of dates"

Now, i hope everyone is sitting down, because this is going to blow your minds, but there exists series of repeating dates that were not generated with an RRULE in the first place.

(Not just being facetious; i was surprised myself that the library system in a significant US city would not be using RRULE internally, but to be very concrete about the real-world need for this capability, it does not and we'll close with an example from it.)

So to be very clear, i'm not looking for the 'best' RRULE for a sequence of datetimes, which would be a judgement call anyway. I'm looking for any valid RRULE automatically produced from a list of repeating dates. For example: A fine start would be checking if it's daily and, failing that, producing the ugliest RRULE known to human or machine, that's just a string of exceptions.

And maybe that's another way to approach this: suggestions or examples from when humans are using an RRULE widget to provide the series of events that aren't following nice repeating rules. Do you fall back to just allowing additional dates?

Anyhow, here's that promised real-world example of an array of dates that will have to be regularly dealt with that do not derive from an RRULE themselves, but could probably messily be reduced to one:

    "future_dates": [
        {
            "event_id": 4998685,
            "start": "2019-10-01T10:30:00-04:00"
        },
        {
            "event_id": 4998686,
            "start": "2019-10-08T10:30:00-04:00"
        },
        {
            "event_id": 4998687,
            "start": "2019-10-15T10:30:00-04:00"
        },
        {
            "event_id": 4998688,
            "start": "2019-10-22T10:30:00-04:00"
        },
        {
            "event_id": 4998689,
            "start": "2019-10-29T10:30:00-04:00"
        },
        {
            "event_id": 4998690,
            "start": "2019-11-05T10:30:00-05:00"
        },
        {
            "event_id": 4998691,
            "start": "2019-11-12T10:30:00-05:00"
        },
        {
            "event_id": 4998692,
            "start": "2019-11-19T10:30:00-05:00"
        },
        {
            "event_id": 4998693,
            "start": "2019-11-26T10:30:00-05:00"
        },
        {
            "event_id": 4998694,
            "start": "2019-12-03T10:30:00-05:00"
        },
        {
            "event_id": 4998695,
            "start": "2019-12-10T10:30:00-05:00"
        },
        {
            "event_id": 4998696,
            "start": "2019-12-17T10:30:00-05:00"
        },
        {
            "event_id": 4998698,
            "start": "2019-12-31T10:30:00-05:00"
        },
        {
            "event_id": 4998699,
            "start": "2020-01-07T10:30:00-05:00"
        },
        {
            "event_id": 4998700,
            "start": "2020-01-14T10:30:00-05:00"
        },
        {
            "event_id": 4998701,
            "start": "2020-01-21T10:30:00-05:00"
        },
        {
            "event_id": 4998702,
            "start": "2020-01-28T10:30:00-05:00"
        },
        {
            "event_id": 4998703,
            "start": "2020-02-04T10:30:00-05:00"
        },
        {
            "event_id": 4998704,
            "start": "2020-02-11T10:30:00-05:00"
        },
        {
            "event_id": 4998705,
            "start": "2020-02-18T10:30:00-05:00"
        },
        {
            "event_id": 4998706,
            "start": "2020-02-25T10:30:00-05:00"
        },
        {
            "event_id": 4998707,
            "start": "2020-03-03T10:30:00-05:00"
        },
        {
            "event_id": 4998708,
            "start": "2020-03-10T10:30:00-04:00"
        },
        {
            "event_id": 4998709,
            "start": "2020-03-17T10:30:00-04:00"
        },
        {
            "event_id": 4998710,
            "start": "2020-03-24T10:30:00-04:00"
        },
        {
            "event_id": 4998711,
            "start": "2020-03-31T10:30:00-04:00"
        },
        {
            "event_id": 4998712,
            "start": "2020-04-07T10:30:00-04:00"
        },
        {
            "event_id": 4998713,
            "start": "2020-04-14T10:30:00-04:00"
        },
        {
            "event_id": 4998714,
            "start": "2020-04-21T10:30:00-04:00"
        },
        {
            "event_id": 4998715,
            "start": "2020-04-28T10:30:00-04:00"
        },
        {
            "event_id": 4998716,
            "start": "2020-05-05T10:30:00-04:00"
        },
        {
            "event_id": 4998717,
            "start": "2020-05-12T10:30:00-04:00"
        },
        {
            "event_id": 4998718,
            "start": "2020-05-19T10:30:00-04:00"
        },
        {
            "event_id": 4998719,
            "start": "2020-05-26T10:30:00-04:00"
        },
        {
            "event_id": 4998720,
            "start": "2020-06-02T10:30:00-04:00"
        },
        {
            "event_id": 4998721,
            "start": "2020-06-09T10:30:00-04:00"
        },
        {
            "event_id": 4998722,
            "start": "2020-06-16T10:30:00-04:00"
        },
        {
            "event_id": 4998723,
            "start": "2020-06-23T10:30:00-04:00"
        },
        {
            "event_id": 4998724,
            "start": "2020-06-30T10:30:00-04:00"
        },
        {
            "event_id": 4998725,
            "start": "2020-07-07T10:30:00-04:00"
        },
        {
            "event_id": 4998726,
            "start": "2020-07-14T10:30:00-04:00"
        },
        {
            "event_id": 4998727,
            "start": "2020-07-21T10:30:00-04:00"
        },
        {
            "event_id": 4998728,
            "start": "2020-07-28T10:30:00-04:00"
        },
        {
            "event_id": 4998729,
            "start": "2020-08-04T10:30:00-04:00"
        },
        {
            "event_id": 4998730,
            "start": "2020-08-11T10:30:00-04:00"
        },
        {
            "event_id": 4998731,
            "start": "2020-08-18T10:30:00-04:00"
        },
        {
            "event_id": 4998732,
            "start": "2020-08-25T10:30:00-04:00"
        },
        {
            "event_id": 4998733,
            "start": "2020-09-01T10:30:00-04:00"
        },
        {
            "event_id": 4998734,
            "start": "2020-09-08T10:30:00-04:00"
        },
        {
            "event_id": 4998735,
            "start": "2020-09-15T10:30:00-04:00"
        },
        {
            "event_id": 4998736,
            "start": "2020-09-22T10:30:00-04:00"
        },
        {
            "event_id": 4998737,
            "start": "2020-09-29T10:30:00-04:00"
        },
        {
            "event_id": 4998738,
            "start": "2020-10-06T10:30:00-04:00"
        },
        {
            "event_id": 4998739,
            "start": "2020-10-13T10:30:00-04:00"
        },
        {
            "event_id": 4998740,
            "start": "2020-10-20T10:30:00-04:00"
        },
        {
            "event_id": 4998741,
            "start": "2020-10-27T10:30:00-04:00"
        },
        {
            "event_id": 4998742,
            "start": "2020-11-03T10:30:00-05:00"
        },
        {
            "event_id": 4998743,
            "start": "2020-11-10T10:30:00-05:00"
        },
        {
            "event_id": 4998744,
            "start": "2020-11-17T10:30:00-05:00"
        },
        {
            "event_id": 4998745,
            "start": "2020-11-24T10:30:00-05:00"
        },
        {
            "event_id": 4998746,
            "start": "2020-12-01T10:30:00-05:00"
        },
        {
            "event_id": 4998747,
            "start": "2020-12-08T10:30:00-05:00"
        },
        {
            "event_id": 4998748,
            "start": "2020-12-15T10:30:00-05:00"
        },
        {
            "event_id": 4998749,
            "start": "2020-12-22T10:30:00-05:00"
        },
        {
            "event_id": 4998750,
            "start": "2020-12-29T10:30:00-05:00"
        },
        {
            "event_id": 4998751,
            "start": "2021-01-05T10:30:00-05:00"
        },
        {
            "event_id": 4998752,
            "start": "2021-01-12T10:30:00-05:00"
        }
    ]
mlncn
  • 3,308
  • 2
  • 23
  • 20

2 Answers2

1

If you have a totally arbitrary list of occurrences, you probably want to use a set of RDATE instead of RRULE. See https://www.rfc-editor.org/rfc/rfc5545#section-3.8.5.2

Community
  • 1
  • 1
Arnaud Quillaud
  • 4,420
  • 1
  • 12
  • 8
1

I have some notes, but not the code, from implementing RRULE generation for a system that didn't use RRULE internally, but that did allow the user to create a series of events. This was a first cut, "good enough" to communicate with another system but potentially ugly.

Before I get into that, it does matter what you intend to do with your RRULE. If your goal is to export the events from one calendaring system and import them into another, you just need to come up with an approach that works for the target system -- and a set of RDATEs might be just the ticket!

If, on the other hand, you want to provide iCalendar files, perhaps as email attachments, to work with arbitrary calendar clients, be warned that many popular clients don't implement RFC5545 completely. Apple calendar, in particular, does not support RDATE (as noted in https://apple.stackexchange.com/questions/68535/does-os-x-calendar-n%C3%A9e-ical-support-rdate). We tested against Google Calendar, Outlook (some versions), and Apple, and concluded that an RRULE with some dates excluded via EXDATE was well supported by all.

The general concept (from my notes) was to take the set of events and try to find a pattern that fit from among the options the source system supported, from sparsest to densest. (For example, start with YEARLY patterns and work down to DAILY.) Consider a pattern a "match" if it covers all the events, even if there are extra dates in the pattern. You can then exclude the "extra" dates by using EXDATE. In the case of a hard-to-match set of dates, this might result in an RRULE for a DAILY pattern accompanied by a large list of EXDATEs. (iCalendar supports frequencies as small as SECONDLY, but for our purposes and I suspect yours, DAILY was the smallest interval expected).

Another thing worth knowing: in iCalendar, you can specify a rescheduled event as an additional VEVENT with its own date/times and a RECURRENCE-ID set to the date/time the event would have started, had it not been rescheduled. This is a good way to handle events that match a pattern in terms of dates, but that have been moved to a different time of day. So, consider building your RRULE up based just on the dates of the events without the time of day, then add exceptions for any time adjustments.

That is assuming your system maintains the association between an event and the rest of its series when it's been rescheduled. If it doesn't, that simplifies your task considerably -- the rescheduled event will simply leave a hole in the pattern, for which you add an EXDATE.

Good luck!

A.K. Farrell
  • 270
  • 3
  • 9