15th Feb 2022 6 minutes read Working With iCalendar in Python Xavier Rigoulet python Python modules In my previous article on How to Work With the Calendar and Arrow Python Modules, we explored how to use calendars in Python. But what about iCal files? In this article, we will discuss how to work with iCalendar in Python, write and read iCal files, and parse a calendar from a URL like Google Calendar. But first, what is an iCal file? The iCalendar (Internet Calendaring and Scheduling Core Object Specification) format allows users to store and exchange calendaring and scheduling information such as events and to-dos. Many products including Google Calendar and Apple Calendar use this format. Let’s take a look at two popular Python modules that simplify working with .ics files. How to Read and Write iCalendar Files in Python: The iCalendar Module icalendar is a popular and convenient library for processing calendars in Python. You need to install it from the pip package: pip install icalendar Take a look at its documentation here. Next, let’s write the code to create a new event and store it on the disk. We start with the imports. In addition to icalendar, we need datetime, pytz, and pathlib. You can read the details about these packages here and here. # imports from icalendar import Calendar, Event, vCalAddress, vText from datetime import datetime from pathlib import Path import os import pytz # init the calendar cal = Calendar() iCalendar follows the RFC5545 specifications, which means we need to include some properties such as PRODID and Version. PRODID specifies the identifier that created the iCalendar object. # Some properties are required to be compliant cal.add('prodid', '-//My calendar product//example.com//') cal.add('version', '2.0') Next, we add the event with a name, a description, a start, and an end. # Add subcomponents event = Event() event.add('name', 'Awesome Meeting') event.add('description', 'Define the roadmap of our awesome project') event.add('dtstart', datetime(2022, 1, 25, 8, 0, 0, tzinfo=pytz.utc)) event.add('dtend', datetime(2022, 1, 25, 10, 0, 0, tzinfo=pytz.utc)) # Add the organizer organizer = vCalAddress('MAILTO:jdoe@example.com') # Add parameters of the event organizer.params['name'] = vText('John Doe') organizer.params['role'] = vText('CEO') event['organizer'] = organizer event['location'] = vText('New York, USA') event['uid'] = '2022125T111010/272356262376@example.com' event.add('priority', 5) attendee = vCalAddress('MAILTO:rdoe@example.com') attendee.params['name'] = vText('Richard Roe') attendee.params['role'] = vText('REQ-PARTICIPANT') event.add('attendee', attendee, encode=0) attendee = vCalAddress('MAILTO:jsmith@example.com') attendee.params['name'] = vText('John Smith') attendee.params['role'] = vText('REQ-PARTICIPANT') event.add('attendee', attendee, encode=0) # Add the event to the calendar cal.add_component(event) Finally, we store the event on the disk as example.ics. We create a directory called MyCalendar with pathlib, then we write the event to a file. You can read one of my previous articles here to learn more about writing to a file in Python. # Write to disk directory = Path.cwd() / 'MyCalendar' try: directory.mkdir(parents=True, exist_ok=False) except FileExistsError: print("Folder already exists") else: print("Folder was created") f = open(os.path.join(directory, 'example.ics'), 'wb') f.write(cal.to_ical()) f.close() Now that we have created an event, let’s read it. e = open('MyCalendar/example.ics', 'rb') ecal = icalendar.Calendar.from_ical(e.read()) for component in ecal.walk(): print(component.name) e.close() The output shows two components: VCALENDAR VEVENT Events are stored in VEVENT. To get the details, we need to access its subcomponents. Note that you need to call the decoded() method instead of get() to output the date and time values. e = open('MyCalendar/example.ics', 'rb') ecal = icalendar.Calendar.from_ical(e.read()) for component in ecal.walk(): if component.name == "VEVENT": print(component.get("name")) print(component.get("description")) print(component.get("organizer")) print(component.get("location")) print(component.decoded("dtstart")) print(component.decoded("dtend")) e.close() And here is the output: Awesome Meeting Define the roadmap of our awesome project MAILTO:jdoe@example.com New York, USA 2022-01-25 08:00:00+00:00 2022-01-25 10:00:00+00:00 There you go! We know how to create an iCal file and read it. Refer to the iCalendar documentation to learn more about the package. Next, let’s learn about processing a calendar in Python. Parse a Google Calendar From a URL: The ics Module We can also parse a Google Calendar URL. For this example, we use the ics.py package, which you can install with the following command: pip install ics Let’s use a public Google calendar about the phases of the Moon, parse it as an iCal file, and extract the events. from ics import Calendar import requests # Parse the URL url = "https://calendar.google.com/calendar/ical/ht3jlfaac5lfd6263ulfh4tql8%40group.calendar.google.com/public/basic.ics" cal = Calendar(requests.get(url).text) # Print all the events print(cal.events) And here is a part of the output: {, , , , , , , , , , , , , , ,...} Want to pretty-print it? Take a look at two popular modules for pretty-printing tables here. Let’s sort the events in descending order using sorted(). We use sorted() instead of fsort() because the events variable is a set, while sort() works with lists. events = cal.events sorted_events = sorted(events, reverse = True) sorted_events Here is a part of the output: [, , , , , , , , , , , , , , , , ,...] You can learn additional details about sort(), sorted(), and more in my article about working with streams in Python. Feel free to reuse these code snippets and explore the ics.py documentation here. The ics module can also create iCalendar files. Say you want to add some events to your work calendar to reserve some time each day for your tasks. You want 2 hours each day, and another 1 hour would be nice-to-have. You can do this with the following code: import arrow from ics import Calendar, Event calendar = Calendar() tz = 'Europe/Paris' first_day = arrow.get("2022-02-14").replace(tzinfo=tz) last_day = arrow.get("2022-02-18").replace(tzinfo=tz) for day in arrow.Arrow.range('day', first_day, last_day): event = Event() event.name = "Working on the task" event.begin = day.replace(hour=8).to('utc').datetime event.end = day.replace(hour=10).to('utc').datetime event.transparent = False calendar.events.add(event) event = Event() event.name = "Continue on the task?" event.begin = day.replace(hour=10).to('utc').datetime event.end = day.replace(hour=11).to('utc').datetime event.transparent = True calendar.events.add(event) print(calendar) # you can simply save this to a file Pretty simple, right? You don't have to worry about event IDs or other required iCalendar file attributes. The transparent value defines your availability. True means "transparent" (i.e., "free"), and False means "opaque" (i.e., "busy"). It may not be obvious, but think of it this way: it either needs to show or block the time based on your availability when you search for free/busy times on your calendar. Also, pay attention to the time zones. The ics module works with UTC values, and you want to set yours before the conversion. Now, you just have to go to your Google Calendar's settings and import the ics file. You may want to test it on a separate test calendar before adding it to your main calendar as there isn't an easy way to bulk-delete events. If you want to learn more about data manipulation and programming with Python, I encourage you to join our Python programming track. Work With iCalendar in Python and More! We have reviewed how to work with iCalendar files in Python in this article. We have discovered how to create an event, store it as an iCal file, and read it. We have also learned about processing a calendar from a URL and extracting the events from a public Google calendar. Now, it’s time for you to play with the code to solidify your knowledge. You can also read about good Python practices in my article here to improve your skills. And learn more Python on LearnPython.com! Tags: python Python modules