Skip to content

Commit

Permalink
Add support of multiple RRULE
Browse files Browse the repository at this point in the history
Add support of multiple RRULE

Add support of multiple RRULE

Add support of multiple RRULE

Add support of multiple RRULE
  • Loading branch information
fabien-michel committed Apr 2, 2024
1 parent 76530d8 commit 85d86ac
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 15 deletions.
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,10 @@ To release new versions,
Changelog
---------

- Incoming

- Add support for multiple RRULE in events.

- v2.2.0

- Add ``after()`` method to iterate over upcoming events.
Expand Down
36 changes: 21 additions & 15 deletions recurring_ical_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,25 +214,31 @@ def __init__(self, component, keep_recurrence_attributes=False):

self.duration = self.end - self.start
self.rule = rule = rruleset(cache=True)
_rule = component.get("RRULE", None)
if _rule:
# We don't support multiple RRULE yet, but we can support cases
# where the same RRULE is erroneously repeated
if isinstance(_rule, list):
if len(_rule) > 0 and all(part == _rule[0] for part in _rule):
_rule = _rule[0]
else:
raise ValueError("Don't yet support multiple distinct RRULE properties")
self.rrule = self.create_rule_with_start(_rule.to_ical().decode())
rule.rrule(self.rrule)
else:
self.rrule = None
_rules = component.get("RRULE", None)
self.rrules = []
if _rules:
if not isinstance(_rules, list):
_rules = [_rules]
else:
_dedup_rules=[]
for _rule in _rules:
if _rule not in _dedup_rules:
_dedup_rules.append(_rule)
_rules = _dedup_rules

for _rule in _rules:
rrule = self.create_rule_with_start(_rule.to_ical().decode())
self.rrules.append(rrule)
rule.rrule(rrule)

for exdate in self.exdates:
rule.exdate(exdate)
for rdate in self.rdates:
rule.rdate(rdate)
if not self.rrule or not self.rrule.until or not compare_greater(self.start, self.rrule.until):

untils = [rrule.until for rrule in self.rrules if rrule and rrule.until]
self.max_until = max(untils) if untils else None
if not self.rrules or not self.max_until or not compare_greater(self.start, self.max_until):
rule.rdate(self.start)

def create_rule_with_start(self, rule_string):
Expand Down Expand Up @@ -339,7 +345,7 @@ def within_days(self, span_start, span_stop):
start = start.tzinfo.localize(start.replace(tzinfo=None))
# We could now well be out of bounce of the end of the UNTIL
# value. This is tested by test/test_issue_20_exdate_ignored.py.
if self.rrule is not None and self.rrule.until is not None and start > self.rrule.until and start not in self.rdates:
if self.rrules is not None and self.max_until is not None and start > self.max_until and start not in self.rdates:
continue
if self._unify_exdate(start) in self.exdates_utc or start.date() in self.exdates_utc:
continue
Expand Down
16 changes: 16 additions & 0 deletions test/calendars/multiple_rrule.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//CyrusIMAP.org/Cyrus
3.9.0-alpha0-85-gd6d859e0cf-fm-20230116.001-gd6d859e0//EN
BEGIN:VEVENT
CREATED:20230109T084023Z
LAST-MODIFIED:20230119T110732Z
DTSTAMP:20230119T110732Z
UID:56cdc4dc-11b7-407c-86c6-9faedfc28afb
SUMMARY:My repeating event
RRULE:FREQ=WEEKLY;BYDAY=TH;COUNT=20
RRULE:FREQ=MONTHLY;BYDAY=2MO;COUNT=2
DTSTART;TZID=Europe/London:20230112T100000
DTEND;TZID=Europe/London:20230112T120000
END:VEVENT
END:VCALENDAR
4 changes: 4 additions & 0 deletions test/test_multiple_rrule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

def test_multiple_rrule(calendars):
events = calendars.multiple_rrule.at(2023)
assert len(events) == 20 + 2

0 comments on commit 85d86ac

Please sign in to comment.