Home > Software engineering >  dateutil rruleset: How to combine EXRULE and RDATE correctly?
dateutil rruleset: How to combine EXRULE and RDATE correctly?

Time:10-14

I have a rruleset with a daily recurrence rule and now I am trying to combine an RDATE with an EXRULE.

from dateutil.rrule import rruleset, rrule, DAILY, FR

rules = rruleset()
daily = rrule(freq=DAILY, dtstart=datetime(2022, 10, 12))
rules.rrule(daily)
not_on_friday = rrule(freq=DAILY, byweekday=FR, dtstart=datetime(2022, 10, 12))
but_on_friday_21th = datetime(2022, 10, 21)
rules.exrule(not_on_friday)
rules.rdate(but_on_friday_21th)
rules.between(datetime(2022,10,12), datetime(2022,10,24))

>>
[datetime.datetime(2022, 10, 13, 0, 0), 
 datetime.datetime(2022, 10, 15, 0, 0), # the 14th is excluded as expected
 datetime.datetime(2022, 10, 16, 0, 0),
 datetime.datetime(2022, 10, 17, 0, 0),
 datetime.datetime(2022, 10, 18, 0, 0),
 datetime.datetime(2022, 10, 19, 0, 0),
 datetime.datetime(2022, 10, 20, 0, 0), 
 datetime.datetime(2022, 10, 22, 0, 0), # but the 21th is also excluded
 datetime.datetime(2022, 10, 23, 0, 0)]

Now, confusingly, when I combine my EXRULE with an EXDATE it works:

rules = rruleset()
daily = rrule(freq=DAILY, dtstart=datetime(2022, 10, 12))
rules.rrule(daily)
not_on_friday = rrule(freq=DAILY, byweekday=FR, dtstart=datetime(2022, 10, 12))
but_also_not_on_the_22th_a_saturday = datetime(2022, 10, 22)
rules.exrule(not_on_friday)
rules.exdate(but_also_not_on_the_22th_a_saturday)
rules.between(datetime(2022,10,12), datetime(2022,10,24))

>>
[datetime.datetime(2022, 10, 13, 0, 0),
 datetime.datetime(2022, 10, 15, 0, 0), # the 14th still excluded
 datetime.datetime(2022, 10, 16, 0, 0),
 datetime.datetime(2022, 10, 17, 0, 0),
 datetime.datetime(2022, 10, 18, 0, 0),
 datetime.datetime(2022, 10, 19, 0, 0),
 datetime.datetime(2022, 10, 20, 0, 0), # the 22th also excluded as expected
 datetime.datetime(2022, 10, 23, 0, 0)]

So, if possible at all, how to combine RDATE and EXRULE in my rruleset?

CodePudding user response:

So apparently there is no such thing as an EXRULE in the iCalendar specs. Its just RRULEs. And dateutils exdate function states in the doc string:

def exrule(self, exrule):
""" Include the given rrule instance in the recurrence set exclusion
    list. Dates which are part of the given recurrence rules will not
    be generated, even if some inclusive rrule or rdate matches them.
"""

So, even if I add an RDATE, if it is exclude by a rule added by exrule it will not show up in my occurrences. Same goes for the exdate function, hence my working second example.

CodePudding user response:

In your answer you note that exrule is applied last, after all other inclusive rules which actually does appear to be in the RFC. However, at least in dateutil, you can use an rruleset as the argument to exrule, so to accomplish what you want, you can try filtering out the date that you want included from the rule that gets passed to exrule, like so:

from datetime import datetime
from dateutil.rrule import rruleset, rrule, DAILY, WEEKLY, FR

# Create an rruleset that defaults to every day
rules = rruleset()
daily = rrule(freq=DAILY, dtstart=datetime(2022, 10, 12))
rules.rrule(daily)

# Create an rruleset corresponding to the days we want to *exclude*: every
# Friday, except 2022-10-21
ex_set = rruleset()
ex_set.rrule(rrule(freq=WEEKLY, byweekday=FR, dtstart=datetime(2022, 10, 14)))
ex_set.exdate(datetime(2022, 10, 21))

# Use our second rule set as an exrule
rules.exrule(ex_set)

rules.between(datetime(2022,10,12), datetime(2022,10,24))

Since the date you want to include never appears in the exrule, it is not filtered out:

>>> print("\n".join(map(str,
...                     map(datetime.date,
...                         rules.between(datetime(2022, 10, 12),
...                                       datetime(2022, 10, 24))))))
2022-10-13
2022-10-15
2022-10-16
2022-10-17
2022-10-18
2022-10-19
2022-10-20
2022-10-21
2022-10-22
2022-10-23
  • Related