Skip to content

Commit

Permalink
Add possibility to add additional dates to recurring events (#45)
Browse files Browse the repository at this point in the history
* Ignore new line at end of file (it breaks the custom ics parser)
* Add possibility to add additional dates to recurring events
* Incorporate feedback from @stefanv. Thank you!
* Remove YAML whitespace that is actually not needed
  • Loading branch information
itrich authored Jan 31, 2023
1 parent 50e0c70 commit b3279c5
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 26 deletions.
4 changes: 3 additions & 1 deletion example/test_calendar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ events:
weeks: 1
until: 2022-12-31 # required

- summary: Recurring event with exception
- summary: Recurring event with exception and additional date
begin: 2022-07-01 10:00:00
duration: { minutes: 60 }
repeat:
Expand All @@ -44,6 +44,8 @@ events:
except_on:
- 2022-07-13
- 2022-07-14 06:00:00
also_on:
- 2022-12-24 06:00:00

# All-day event
- summary: Earth Day
Expand Down
5 changes: 4 additions & 1 deletion tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def test_exception():
event = event_from_yaml(
parse_yaml(
"""
summary: Recurring event with exception
summary: Recurring event with exception and additional date
timezone: America/Los_Angeles
begin: 2022-07-01 10:00:00
duration: {minutes: 60}
Expand All @@ -80,6 +80,8 @@ def test_exception():
except_on:
- 2022-07-13
- 2022-07-14 06:00:00
also_on:
- 2022-12-24 06:00:00
"""
)
)
Expand All @@ -90,6 +92,7 @@ def test_exception():
"EXDATE;TZID=/ics.py/2020.1/America/Los_Angeles:20220713,20220714T060000"
in event_str
)
assert "RDATE;TZID=/ics.py/2020.1/America/Los_Angeles:20221224T060000" in event_str


def test_event_with_time_range():
Expand Down
50 changes: 26 additions & 24 deletions yaml2ics.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,26 @@
}


def strfexception(exdate):
if isinstance(exdate, datetime.datetime):
return datetime.datetime.strftime(exdate, "%Y%m%dT%H%M%S")
elif isinstance(exdate, datetime.date):
return datetime.datetime.strftime(exdate, "%Y%m%d")
def datetime2utc(date):
if isinstance(date, datetime.datetime):
return datetime.datetime.strftime(date, "%Y%m%dT%H%M%S")
elif isinstance(date, datetime.date):
return datetime.datetime.strftime(date, "%Y%m%d")


# See RFC2445, 4.8.5 REcurrence Component Properties
# This function can be used to add a list of e.g. exception dates (EXDATE) or recurrence dates (RDATE)
# to a reoccurring event
def add_recurrence_property(
event: ics.Event, property_name, dates: map, tz: datetime.tzinfo = None
):
event.extra.append(
ics.ContentLine(
name=property_name,
params={"TZID": [str(ics.Timezone.from_tzinfo(tz))]} if tz else None,
value=",".join(dates),
)
)


def event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event:
Expand Down Expand Up @@ -100,25 +115,12 @@ def event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event:
)

if "except_on" in repeat:
exdates = map(
lambda exdate: strfexception(exdate),
repeat["except_on"],
)
if tz:
event.extra.append(
ics.ContentLine(
name="EXDATE",
params={"TZID": [str(ics.Timezone.from_tzinfo(tz))]},
value=",".join(exdates),
)
)
else:
event.extra.append(
ics.ContentLine(
name="EXDATE",
value=",".join(exdates),
)
)
exdates = [datetime2utc(rdate) for rdate in repeat["except_on"]]
add_recurrence_property(event, "EXDATE", exdates, tz)

if "also_on" in repeat:
rdates = [datetime2utc(rdate) for rdate in repeat["also_on"]]
add_recurrence_property(event, "RDATE", rdates, tz)

event.dtstamp = datetime.datetime.utcnow().replace(tzinfo=dateutil.tz.UTC)
if tz and event.floating and not event.all_day:
Expand Down

0 comments on commit b3279c5

Please sign in to comment.