Skip to content

Commit

Permalink
Merge pull request #49 from niccokunzmann/issue-48
Browse files Browse the repository at this point in the history
#48 Daylight aware repeats
  • Loading branch information
niccokunzmann authored Dec 17, 2020
2 parents a47f7b0 + f195660 commit 8f1d7ab
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 5 deletions.
17 changes: 14 additions & 3 deletions recurring_ical_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ def as_vevent(self):

def is_in_span(self, span_start, span_stop):
return time_span_contains_event(span_start, span_stop, self.start, self.stop)

def __repr__(self):
return "{}({{'UID':{}...}}, {}, {})".format(self.__class__.__name__, self.source.get("UID"), self.start, self.stop)

def __init__(self, event):
self.event = event
Expand Down Expand Up @@ -220,7 +223,7 @@ def _unify_exdate(self, dt):
return timestamp(dt)
return dt

def within(self, span_start, span_stop):
def within_days(self, span_start, span_stop):
# make dates comparable, rrule converts them to datetimes
span_start = convert_to_datetime(span_start, self.tzinfo)
span_stop = convert_to_datetime(span_stop, self.tzinfo)
Expand All @@ -231,10 +234,11 @@ def within(self, span_start, span_stop):
# may still be mixed because RDATE, EXDATE, start and rule.
for start in self.rule.between(span_start, span_stop, inc=True):
if isinstance(start, datetime.datetime) and start.tzinfo is not None:
# update the time zone in case of summer/winter time change
start = start.tzinfo.localize(start.replace(tzinfo=None))
stop = start + self.duration
if self._unify_exdate(start) in self.exdates_utc:
continue
stop = start + self.duration
yield self.Repetition(
self.event,
self.convert_to_original_type(start),
Expand Down Expand Up @@ -350,8 +354,15 @@ def add_event(event):
same_events[recurrence_id] = event
events.append(event)

span_start_day = span_start
if isinstance(span_start_day, datetime.datetime):
span_start_day = span_start_day.replace(hour=0,minute=0,second=0)
span_stop_day = span_stop
if isinstance(span_stop_day, datetime.datetime):
span_stop_day = span_stop.replace(hour=23,minute=59,second=59)

for event_repetions in self.repetitions:
for repetition in event_repetions.within(span_start, span_stop):
for repetition in event_repetions.within_days(span_start_day, span_stop_day):
if compare_greater(repetition.start, span_stop):
break
if repetition.is_in_span(span_start, span_stop):
Expand Down
43 changes: 43 additions & 0 deletions test/calendars/issue_48_daylight_aware_repeats.ics
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Horario sem-5
X-WR-TIMEZONE:Europe/Lisbon
BEGIN:VTIMEZONE
TZID:Europe/Lisbon
X-LIC-LOCATION:Europe/Lisbon
BEGIN:STANDARD
TZOFFSETFROM:+0100
TZOFFSETTO:+0000
TZNAME:WET
DTSTART:19701025T020000
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
END:STANDARD
BEGIN:DAYLIGHT
TZOFFSETFROM:+0000
TZOFFSETTO:+0100
TZNAME:WEST
DTSTART:19700329T010000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
DTSTART;TZID=Europe/Lisbon:20200921T113000
DTEND;TZID=Europe/Lisbon:20200921T130000
RRULE:FREQ=WEEKLY;BYDAY=MO
DTSTAMP:20201026T103342Z
UID:EVENT2
CREATED:20200920T235116Z
DESCRIPTION:<p><a href="https://videoconf-colibri.zoom.us/j/93800310242?pwd
=K3FBUVJUV2hrTm1OR2RWb0ZabTJkZz09" target="_blank">https://videoconf-colibr
i.zoom.us/j/93800310242?pwd=K3FBUVJUV2hrTm1OR2RWb0ZabTJkZz09</a></p>
LAST-MODIFIED:20200920T235214Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:MDS-t
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
5 changes: 3 additions & 2 deletions test/test_daylight_saving_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
berlin = timezone("Europe/Berlin")

@pytest.mark.parametrize("date,time", [
((2019,3,20), berlin.localize(datetime.datetime(2019, 3, 20, 19))),
((2019,4,24), berlin.localize(datetime.datetime(2019, 4, 24, 19))),
((2019,3,20), berlin.localize(datetime.datetime(2019, 3, 20, 19))), # winter time, UTC+1
((2019,4,24), berlin.localize(datetime.datetime(2019, 4, 24, 19))), # summer time UTC+2
])
def test_daylight_saving_events(calendars, date, time):
"""Test the event 7uartkcnhf0elbvs8md0itrf6c@google.com."""
event = calendars.daylight_saving_time.at(date)[0]
assert event["DTSTART"].dt == time
34 changes: 34 additions & 0 deletions test/test_issue_48_daylight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""These tests are for Issue 48
https://github.com/niccokunzmann/python-recurring-ical-events/issues/48
These were the events occurring when the issue was raised:
EVENT2:
start: 2020-11-02 11:30:00+00:00
stop: 2020-11-02 13:00:00+00:00
March to October: UTC+1
October to March: UTC+0
"""

import pytest
from pytz import timezone
from datetime import datetime, date

TZ = timezone("Europe/Lisbon")
@pytest.mark.parametrize("date,event_name",
[(datetime(2020,11,2,11,15,0,0,TZ),0),
(datetime(2020,11,2,11,31,0,0,TZ),"EVENT2"),
(datetime(2020,11,2,12,0,0,0,TZ),"EVENT2"),
(datetime(2020,11,2,12,1,0,0,TZ),"EVENT2"),
(datetime(2020,11,2,13,0,0,0,TZ),"EVENT2"),
(datetime(2020,11,2,13,1,0,0,TZ),0)
])
def test_event_timing(calendars,date,event_name):
date = date.tzinfo.localize(date.replace(tzinfo= None))
events = calendars.issue_48_daylight_aware_repeats.at(date)
if event_name:
assert len(events) == 1
assert events[0]["UID"] == event_name
else:
assert not events, "no events expected"

0 comments on commit 8f1d7ab

Please sign in to comment.