Round-trip iCal (RFC 5545) read + write for booking/availability calendars, in under 200 lines.
Project description
pantheon-ical
Round-trip iCal (RFC 5545) read + write for booking / availability calendars — in under 200 lines.
The guarantee: import busy dates from the systems that speak iCal (Airbnb, Booking.com, Google Calendar, Vrbo, …), and export your own busy dates back as a feed they import — with the same half-open date model on both sides, so a block on any channel propagates to the others without double-booking.
Extracted from PANTHEON, where it drives two-way calendar sync for the holiday-let and appointment businesses it hosts.
Most iCal libraries are large, general-purpose, and read-only. This is the small, opinionated subset you actually need to keep an availability calendar in sync — and it does the export side too, which is the half most libraries skip.
What it does
from pantheon_ical import parse_ical, build_ical, parse_ical_slots
# 1. Import: an OTA feed → busy DATE ranges (half-open, checkout day exclusive)
busy = parse_ical(text) # [(date(2026,7,1), date(2026,7,5)), ...] — the 1st–5th = 4 nights, 5th free
# 2. Export: your busy ranges → a VCALENDAR feed OTHER systems import
feed = build_ical(busy, uid_ns="my-listing")
# → hand this URL to Booking.com/Airbnb "Import calendar" and your blocks propagate outward
# 3. Appointments: DATETIME slots, timezone-aware (for class/booking calendars)
slots = parse_ical_slots(text) # [(datetime, datetime), ...]
Design choices that matter (learned against real OTA feeds)
- Half-open intervals, checkout-day exclusive.
DTSTART:20260701 .. DTEND:20260705blocks four nights and leaves the 5th free for the next arrival — matching how every booking system models a stay. - RRULE recurrence is expanded around now, not DTSTART. An "every Monday since 2024" rule has a years-old start date; a naive expander would only produce historical dates and leave the live window bookable. This anchors the horizon to the present.
- Unparseable rules fall back to the master occurrence. A recurrence syntax we don't model can never silently under-block real availability (which is what causes a double-booking) — the safe failure direction.
build_icalis deterministic (no wall-clock) and emits only busy dates + a fixed generic summary — so a public feed leaks no guest data, and an unchanged calendar is byte-identical (ETag/cache friendly).
Install
pip install pantheon-ical # or copy the single pantheon_ical.py file
Depends only on python-dateutil (for RRULE expansion).
Changelog
- 0.1.1 — DTSTART+DURATION support (OTA feeds emit it) so a durational booking blocks its full span, not a single night (the exact silent under-block → double-book this guards against); and the RRULE expansion fallback now logs a warning instead of failing silently, so a broken/missing dateutil is visible rather than quietly under-blocking recurring dates.
- 0.1.0 — initial release.
License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pantheon_ical-0.1.1.tar.gz.
File metadata
- Download URL: pantheon_ical-0.1.1.tar.gz
- Upload date:
- Size: 12.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ef6a754decc75a37d8a597decef04b095dff6d84028175af43bfc669db3d73a5
|
|
| MD5 |
d0be1ecf0e71f794ebd12effa18fa418
|
|
| BLAKE2b-256 |
7ca0632fe8fb399fea0fb2f2522ba9336c499663fdbbefb75a68e3e9cbe3d906
|
Provenance
The following attestation bundles were made for pantheon_ical-0.1.1.tar.gz:
Publisher:
publish.yml on Igfray/pantheon-ical
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pantheon_ical-0.1.1.tar.gz -
Subject digest:
ef6a754decc75a37d8a597decef04b095dff6d84028175af43bfc669db3d73a5 - Sigstore transparency entry: 2163724537
- Sigstore integration time:
-
Permalink:
Igfray/pantheon-ical@7e265c7955105cb082d15561d74cfd611b115508 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Igfray
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7e265c7955105cb082d15561d74cfd611b115508 -
Trigger Event:
push
-
Statement type:
File details
Details for the file pantheon_ical-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pantheon_ical-0.1.1-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7138d63b5617f60b223fb16312ed77bffc34acdcb9aac98831cca95fb4ca7d78
|
|
| MD5 |
69d55936160eb42fbacd5540e52d3fe2
|
|
| BLAKE2b-256 |
619d6c4257765e98790874f3d7f6154670daba9ae7f629b02de383b1cf7b39d1
|
Provenance
The following attestation bundles were made for pantheon_ical-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on Igfray/pantheon-ical
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pantheon_ical-0.1.1-py3-none-any.whl -
Subject digest:
7138d63b5617f60b223fb16312ed77bffc34acdcb9aac98831cca95fb4ca7d78 - Sigstore transparency entry: 2163724548
- Sigstore integration time:
-
Permalink:
Igfray/pantheon-ical@7e265c7955105cb082d15561d74cfd611b115508 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/Igfray
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7e265c7955105cb082d15561d74cfd611b115508 -
Trigger Event:
push
-
Statement type: