niccokunzmann / python-recurring-ical-events

Python library to calculate recurrence times of events, todos and journals based on icalendar RFC5545
https://pypi.org/project/recurring-ical-events/
GNU Lesser General Public License v3.0
92 stars 20 forks source link

bug: EXDATE does not work for event with higher SEQUENCE #148

Closed zoifar closed 2 months ago

zoifar commented 3 months ago

Describe the bug

EXDATE does not exclude an instance for an event with higher SEQUENCE and the same UID. **To Reproduce** ``` ical_string="""BEGIN:VCALENDAR BEGIN:VEVENT DTSTAMP:20240707T214014Z DTSTART;VALUE=DATE:20240701 DTEND;VALUE=DATE:20240708 SUMMARY:test123 CATEGORIES:other UID:111 ORGANIZER:aaa RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO CREATED:20240311T051101Z LAST-MODIFIED:20240311T051101Z SEQUENCE:1 END:VEVENT BEGIN:VEVENT DTSTAMP:20240707T214014Z DTSTART;VALUE=DATE:20240701 DTEND;VALUE=DATE:20240708 SUMMARY:test123 CATEGORIES:other UID:111 ORGANIZER:aaa RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO EXDATE;VALUE=DATE:20240715 CREATED:20240311T051101Z LAST-MODIFIED:20240701T063743Z SEQUENCE:2 END:VEVENT END:VCALENDAR""" calendar = icalendar.Calendar.from_ical(ical_string) events=recurring_ical_events.of(calendar).at(2024) for event in events: start = event["DTSTART"].dt duration = event["DTEND"].dt - event["DTSTART"].dt print("start {} duration {}".format(start, duration)) ``` **ICS file** ``` BEGIN:VCALENDAR BEGIN:VEVENT DTSTAMP:20240707T214014Z DTSTART;VALUE=DATE:20240701 DTEND;VALUE=DATE:20240708 SUMMARY:test123 CATEGORIES:other UID:111 ORGANIZER:aaa RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO CREATED:20240311T051101Z LAST-MODIFIED:20240311T051101Z SEQUENCE:1 END:VEVENT BEGIN:VEVENT DTSTAMP:20240707T214014Z DTSTART;VALUE=DATE:20240701 DTEND;VALUE=DATE:20240708 SUMMARY:test123 CATEGORIES:other UID:111 ORGANIZER:aaa RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO EXDATE;VALUE=DATE:20240715 CREATED:20240311T051101Z LAST-MODIFIED:20240701T063743Z SEQUENCE:2 END:VEVENT END:VCALENDAR ``` **Expected behavior** only events from SEQUENCE:2 are returned: ``` start 2024-07-01 duration 7 days, 0:00:00 start 2024-07-29 duration 7 days, 0:00:00 ``` **Console output** ``` start 2024-07-15 duration 7 days, 0:00:00 start 2024-07-01 duration 7 days, 0:00:00 start 2024-07-29 duration 7 days, 0:00:00 ``` **Version:** 2.2.1 **Additional context**

This is generated by atlassian confluence calendar when multiple instances of an event are modified.


We're using Polar.sh so you can upvote and help fund this issue. We receive the funding once the issue is completed & confirmed by you. Thank you in advance for helping prioritize & fund our work.

Fund with Polar

niccokunzmann commented 3 months ago

Thanks! This is a nice catch of an edge case!

niccokunzmann commented 2 months ago

For more edge cases:

  1. event made (3x, each 2 weeks)
  2. event in middle moved
  3. base event edited
import icalendar, recurring_ical_events
ical_string="""BEGIN:VCALENDAR
BEGIN:VEVENT
DTSTAMP:20240707T214014Z
DTSTART;VALUE=DATE:20240701
DTEND;VALUE=DATE:20240708
SUMMARY:test123
CATEGORIES:other
UID:111
ORGANIZER:aaa
RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO
CREATED:20240311T051101Z
LAST-MODIFIED:20240311T051101Z
SEQUENCE:1
END:VEVENT
BEGIN:VEVENT
RECURRENCE-ID;VALUE=DATE:20240715
DTSTAMP:20240707T214014Z
DTSTART;VALUE=DATE:20240702
DTEND;VALUE=DATE:20240709
SUMMARY:test123 - edited event!!!!
CATEGORIES:other
UID:111
ORGANIZER:aaa
RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO
CREATED:20240311T051101Z
LAST-MODIFIED:20240311T051101Z
SEQUENCE:2
END:VEVENT
BEGIN:VEVENT
DTSTAMP:20240707T214014Z
DTSTART;VALUE=DATE:20240701
DTEND;VALUE=DATE:20240708
SUMMARY:test123
CATEGORIES:other
UID:111
ORGANIZER:aaa
RRULE:FREQ=WEEKLY;UNTIL=20240801;INTERVAL=2;BYDAY=MO
EXDATE;VALUE=DATE:20240715
CREATED:20240311T051101Z
LAST-MODIFIED:20240701T063743Z
SEQUENCE:3
END:VEVENT
END:VCALENDAR
"""
calendar = icalendar.Calendar.from_ical(ical_string)
events=recurring_ical_events.of(calendar).at(2024)

for event in events:
    start = event["DTSTART"].dt
    duration = event["DTEND"].dt - event["DTSTART"].dt
    print("start {} duration {}".format(start, duration))

current output:

start 2024-07-02 duration 7 days, 0:00:00
start 2024-07-01 duration 7 days, 0:00:00
start 2024-07-29 duration 7 days, 0:00:00
EXDATE;VALUE=DATE:20240715

Can you import this and check?

zoifar commented 2 months ago

Tested 1st case in confluence:

start 2024-07-01 duration 7 days, 0:00:00
start 2024-07-29 duration 7 days, 0:00:00

Also tested 1st case in microsoft and apple:

start 2024-07-02 duration 7 days, 0:00:00
start 2024-07-01 duration 7 days, 0:00:00
start 2024-07-29 duration 7 days, 0:00:00

Tested 2nd case in confluence, microsoft and apple:

start 2024-07-02 duration 7 days, 0:00:00
start 2024-07-01 duration 7 days, 0:00:00
start 2024-07-29 duration 7 days, 0:00:00
niccokunzmann commented 2 months ago

@zoifar Do I see this correctly that confluence and differs from Microsoft and Apple in EDGE CASE 1?

zoifar commented 2 months ago

Just tested 1st case in confluence cloud. Looks like they fixed something in their cloud version of confluence and now it shows the same events as microsoft and apple.

niccokunzmann commented 2 months ago

Yay!

niccokunzmann commented 2 months ago

This is fixed in #149.

niccokunzmann commented 2 months ago

This is released in v2.2.3.