Library / SDK
ics-py/ics-py avatar
ics-py/ics-py

ics.py: reading and writing iCalendar files without touching RFC 5545

Pythonic and easy iCalendar library (rfc5545)

723 stars134 forksPythonNOASSERTION

At a glance

What is it?
The ics package gives Python developers a Calendar and Event API over the .ics format instead of a line-oriented parser, with dates, alarms, attendees and categories exposed as attributes. It suits anyone generating or consuming calendar files in Python, and its development branch has been sitting on version 0.8.0.dev0 for a long while.
Who is it for?
ics.py fits a Python service that produces or consumes .ics files, such as a calendar feed generator or a scheduling export, and it does not fit a project that needs a maintained, standards-complete implementation with a predictable release cadence, since the newest published release is 0.7.2 from 2022.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two versions, two branches, and a README that tells you which you have

The readme opens with a warning that matters more than usual for this project. You are viewing the README for the current development version on the main branch, and to get the README for the current release you are pointed at the PyPi page and at the 0.7 branch of the repository.

The version numbers explain why. The main branch carries 0.8.0.dev0, a development version, and there is an alpha release of 0.8.0.dev0 on PyPi that the readme points to as a slightly older alternative. The published release line stopped at v0.7.2 on 2022-07-06, preceded by v0.7.1 in June 2022 and v0.7 on 2020-02-29, a release named for Katherine Johnson.

The last push to the repository was on 2026-04-15, and the repository is not archived, but that is several months back and the published releases stopped in 2022. The gap between branch activity and releases is what you have to navigate when you pick a dependency version.

The project is at github.com/ics-py/ics-py, the package on PyPI is named ics, and the documentation is hosted at icspy.readthedocs.org. The pyproject declares a hatchling build, a Typing:: Typed classifier, and support for Python 3.8 through 3.12.

The licence situation needs a sentence of care. The README and the pyproject both state Apache 2.0, with an OSI classifier for the Apache Software License, and the repository carries a LICENSE.rst file. The repository's own metadata field is recorded as a custom licence that GitHub cannot classify, so the LICENSE.rst in the repository is the file to read rather than the badge.

What the library actually abstracts away

The stated goal of ics.py is to read and write ics data in a developer friendly way, and the reason given is worth repeating: iCalendar is a widely-used and useful format but not user friendly, and ics.py is there to give you the ability of creating and reading the format without any knowledge of it.

Knowing what that means in practice requires knowing what RFC 5545 looks like. The format is a text format built from content lines of the form NAME;PARAM=value:VALUE, folded at 75 characters, with a specific escaping and quoting rules, a date-time format that is either a floating local time, a UTC time or a time with a timezone reference, and a component hierarchy of VCALENDAR, VEVENT, VTODO, VALARM and VJOURNAL. Getting that wrong produces files that some calendar applications open and others silently ignore.

The library states two parsing goals: it should be able to parse every calendar that respects RFC 5545 and maybe some more, and it also outputs RFC compliant calendars. So the contract is on both sides, reading and writing.

The API surface is what a Python developer would expect. A Calendar object holds events and todos, an Event object has attributes for summary, description, begin, end, location, url, status, organizer, attendees, categories, alarms and attachments, and a serialization step produces the text. The quickstart example in the readme is a complete program, and the object dump it produces shows the whole model in one place: an Event carrying a timespan, a uid, a dtstamp, a list of alarms, a list of attendees, a list of categories and a list of todos at the calendar level.

Your first calendar file, end to end

The quickstart is short enough to read at a glance. You install the package:

bash
pip install ics

Then you build a calendar, add an event with a title, a description and a start and end time, and write the file:

python
 from datetime import datetime
 from ics import Calendar, Event

 c = Calendar()
 e = Event()
 e.summary = "My cool event"
 e.description = "A meaningful description"
 e.begin = datetime.fromisoformat("2022-06-06T12:05:23+02:00")
 e.end = datetime.fromisoformat("2022-06-06T13:05:23+02:00")
 c.events.append(e)
 with open("my.ics", "w", newline='') as f:
     f.write(c.serialize())

Three details in that snippet are worth pausing on. The times are given as ISO 8601 strings with a UTC offset, and they land in the model as a timespan with a real tzinfo, so the file carries an unambiguous instant rather than a floating local time. The write uses newline='' rather than the default, which matters because RFC 5545 folds long lines and a text-mode write on some platforms would translate newlines and corrupt the folding.

The uid is generated for you, with the familiar domain suffix pattern visible in the object dump in the readme, and the dtstamp is set to the current time in UTC, which is what makes the generated file look like something a real calendar application wrote.

The file you get back is a .ics file, the format used by Google Calendar, Apple Calendar, Android and many more, so the output is directly consumable without further conversion.

Dependencies, and why there are only three

The runtime dependencies are listed in the pyproject and they are short: python-dateutil at 2.8 or above, attrs at 20.3 or above, and ics-vtimezones from 2020.1.

Each one is there for a reason, and the reasons explain the design. python-dateutil does the hard work of parsing and normalising the several date and time representations RFC 5545 allows, including the timezone references that map to a VTIMEZONE component in the file. attrs is the reason the model objects can have defaults, validators and equality for free, which is what lets you set e.summary as a plain attribute and still get a well-formed object. ics-vtimezones is a separate package carrying the timezone data, which keeps that data out of the calendar library itself.

The development and test extras are more revealing about the project. The test group includes pytest and pytest-cov, hypothesis for property-based testing, tatsu for parsing, freezegun for controlling the clock, lipsum for generating text, and setuptools. Freezegun and lipsum together point at a specific testing concern: a calendar library has to produce a stable dtstamp, and a property-based generator can produce text with any characters, including the ones that need escaping. Tatsu is a parsing framework, which is what a parser project would reach for when it needs a grammar rather than a pile of regular expressions.

The checks group pins mypy at exactly 0.960 and includes flake8, and the dev group includes tox and bump2version, and the repository root has a.bumpversion.cfg, a tox.ini, a codecov.yml and a.pre-commit-config.yaml. The release numbering is managed by a tool rather than by hand, which is consistent with the versioned branch structure described in the readme.

The alternatives the project itself points at

The readme names two alternative libraries directly, which is more useful than most libraries manage. They are icalendar from the collective organisation and ical from allenporter.

The difference in approach is roughly this. Ics.py goes through the whole file, and it aims at a friendly object model where a calendar is a set of Event objects with named attributes and a serialize method. The alternative approach in icalendar is to treat the file as a structure of components and properties where you ask a calendar for a component by name and then reach into its properties, which stays closer to the wire format and gives you access to properties that no object model has modelled.

So the choice is about where the abstraction sits. Ics.py makes the common case pleasant, where you want a summary, a description, a start and an end, and it decides for you how those become RFC lines. The component-level approach keeps you closer to the format, which is what you want when you are handling properties that the object model does not expose, or when you need a property to round-trip exactly as it came in.

For a reader, that is the real question to ask about ics.py. The library covers the fields an application normally sets, and the object dump in the readme shows how wide that coverage is, but a file that came from elsewhere can contain properties the model does not name. The readme does not document what happens to an unmodelled property on a round trip, and for a calendar library that is the question worth asking before you commit to a feed format you do not fully control.

State of the project: a typed, tested library with a stale release line

Two facts pull in opposite directions, and both matter if you are choosing a dependency.

The engineering signals are good. The package declares itself Typed, ships type hints, uses attrs for its model, keeps the runtime dependency count at three, has property-based testing with hypothesis, runs mypy and flake8 in a dedicated checks group, and carries a codecov configuration and a pre-commit hook setup. The repository layout is disciplined too, with src/ for the package, doc/ for the Sphinx documentation, tests/, a tox.ini for multi-environment runs and a.readthedocs.yaml for the docs build, and the documentation is hosted on readthedocs and updated automatically at every commit.

The release signals are the problem. The newest published version is 0.7.2 from 2022-07-06. The development version is 0.8.0.dev0, which by the project's own naming convention is not a release. The last push was on 2026-04-15, which is not long before this writing, yet the branch and the released artifact are four years apart, and the readme itself warns you that you are reading the development version's documentation.

The development status classifier in the pyproject is Development Status:: 4 - Beta, which is the project telling you plainly what it is.

The practical consequence is a decision about which artifact you depend on. If you want the released 0.7.2, you take the 0.7 branch and the PyPI package, and you get a stable, four-year-old library. If you want the current main branch with 0.8.0.dev0, you install straight from Git, and you are tracking a branch whose API may still move. The readme gives you the command for the second case:

bash
pip install git+https://github.com/ics-py/ics-py.git

Editorial conclusion

ics.py fits a Python service that produces or consumes .ics files, such as a calendar feed generator or a scheduling export, and it does not fit a project that needs a maintained, standards-complete implementation with a predictable release cadence, since the newest published release is 0.7.2 from 2022. Before depending on it, decide whether the 0.7 branch or the main branch is the right base, and check the CHANGELOG.rst on that branch, because the two carry different versions and the difference is not a patch release.

Frequently asked questions

How do I install ics.py and create a calendar file?

Run pip install ics, then create a Calendar and an Event from the ics package, set e.summary, e.description, e.begin and e.end, append the event to c.events, and write c.serialize() to a file opened with newline set to an empty string. The quickstart in the README shows the whole program.

What is the difference between the ics package and the ics.py repository?

The PyPI package is named ics and the repository is ics-py/ics-py. The published release line is 0.7.2 from 2022, while the main branch carries the development version 0.8.0.dev0, and the README on main is explicitly the development version's documentation. The 0.7 branch of the repository holds the current release.

Which Python versions does ics.py support?

The README states availability for Python 3.8, 3.9, 3.10, 3.11 and 3.12, and the pyproject sets requires-python to 3.8 or above with matching classifiers for each version.

What does ics.py depend on at runtime?

Three packages: python-dateutil at 2.8 or above for date and time handling, attrs at 20.3 or above for the model objects, and ics-vtimezones from 2020.1, a separate package carrying the timezone data.

What are the alternatives to ics.py?

The README links two alternative libraries, icalendar from the collective organisation and ical from allenporter. The difference in approach is that ics.py exposes a friendly object model of Calendar and Event objects, while the component-level approach stays closer to the underlying RFC 5545 properties.

Official sources

  1. ics-py/ics-py on GitHub
  2. Issues
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ics-py-ics-py.svg)](https://hysenlabs.com/projects/ics-py-ics-py)

Was this useful?