Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions example/test_calendar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ events:
weeks: 1
until: 2022-12-31 # required

- summary: Recurring event with exception
begin: 2022-07-01 10:00:00
duration: {minutes: 60}
repeat:
interval:
# seconds, minutes, hours, days, weeks, months, years
hours: 4
until: 2022-07-31 # required
except_on:
- 2022-07-13
- 2022-07-14 06:00:00

# All-day event
- summary: Earth Day
begin: 2021-04-22
Expand Down
27 changes: 27 additions & 0 deletions tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,33 @@ def test_rrule():
assert "RRULE:FREQ=YEARLY;UNTIL=20300422T000000" in event_str


def test_exception():
event = event_from_yaml(
parse_yaml(
"""
summary: Recurring event with exception
timezone: America/Los_Angeles
begin: 2022-07-01 10:00:00
duration: {minutes: 60}
repeat:
interval:
hours: 4
until: 2022-07-31
except_on:
- 2022-07-13
- 2022-07-14 06:00:00
"""
)
)
event_str = event.serialize()
# DTEND exists and is the next day, calendar tools import this
# correctly as being a one-day event
assert (
"EXDATE;TZID=/ics.py/2020.1/America/Los_Angeles:20220713,20220714T060000"
in event_str
)


def test_event_with_time_range():
event = event_from_yaml(
parse_yaml(
Expand Down
34 changes: 31 additions & 3 deletions yaml2ics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

CLI to convert yaml into ics.
"""
import datetime
import os
import sys
from datetime import datetime, tzinfo

import dateutil
import dateutil.rrule
Expand All @@ -25,7 +25,14 @@
}


def event_from_yaml(event_yaml: dict, tz: tzinfo = None) -> ics.Event:
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 event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event:
d = event_yaml
repeat = d.pop("repeat", None)
if "timezone" in d:
Expand Down Expand Up @@ -90,7 +97,28 @@ def event_from_yaml(event_yaml: dict, tz: tzinfo = None) -> ics.Event:
)
)

event.dtstamp = datetime.utcnow().replace(tzinfo=dateutil.tz.UTC)
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(
Comment thread
itrich marked this conversation as resolved.
name="EXDATE",
value=",".join(exdates),
)
)

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