A tiny DSL for merging and searching over calendar-like intervals.
pip install calgebra
# Or with Google Calendar support
pip install calgebra[google-calendar]from datetime import datetime, timezone
from calgebra import day_of_week, time_of_day, hours, HOUR
# Compose time windows from primitives
weekdays = day_of_week(["monday", "tuesday", "wednesday", "thursday", "friday"])
work_hours = time_of_day(start=9*HOUR, duration=8*HOUR, tz="US/Pacific")
business_hours = weekdays & work_hours
# Union: combine busy times (in practice: from Google Calendar, databases, etc.)
monday_meetings = day_of_week("monday") & time_of_day(start=10*HOUR, duration=2*HOUR)
friday_focus = day_of_week("friday") & time_of_day(start=14*HOUR, duration=3*HOUR)
busy = monday_meetings | friday_focus
# Difference: find free time during business hours
free = business_hours - busy
# Filter: only slots >= 2 hours
long_slots = free & (hours >= 2)
# Fetch results with slice notation (supports int, datetime, or date)
start = datetime(2025, 1, 1, tzinfo=timezone.utc)
end = datetime(2025, 1, 31, tzinfo=timezone.utc)
meeting_options = list(long_slots[start:end])Intervals in calgebra are inclusive of both start and end—durations therefore reflect every second covered by an interval. Timeline slices accept integer seconds (Unix timestamps), timezone-aware datetime objects, or date objects.
Important: Intervals are automatically clipped to your query bounds. When you slice timeline[start:end], any intervals extending beyond those bounds are trimmed to fit. This ensures aggregations like total_duration() and set operations work correctly within your query window. When you subclass Interval, define your subclass as a dataclass (ideally frozen=True) so the algebra can clone and clamp events internally.
Common helpers and aggregates are exposed alongside the core DSL:
Recurring Patterns (RFC 5545 via python-dateutil):
recurring(freq, ...)generates intervals based on recurrence rules (weekly, bi-weekly, monthly, etc.)day_of_week(days, tz)convenience wrapper for filtering by day(s) of weektime_of_day(start, duration, tz)convenience wrapper for daily time windowsHOUR,MINUTE,DAY,SECONDconstants for readable time specifications- Compose with
&to create complex patterns like business hours, recurring meetings, etc.
Aggregation & Analysis:
flatten(timeline)converts overlapping/adjacent spans into a coalesced timeline (returns maskIntervals and must be sliced with explicit bounds)union(*timelines)/intersection(*timelines)offer functional set operationstotal_durationsums inclusive coverage inside a windowmax_duration/min_durationfind the longest or shortest clamped intervalscount_intervalstallies events over a slicecoverage_ratioreports utilization as a 0–1 fraction
Transformations:
buffer(timeline, before, after)adds buffer time around each interval (useful for travel time, setup/teardown)merge_within(timeline, gap)coalesces intervals separated by at mostgapseconds (useful for grouping related events)
Integrations:
calgebra.gcsa.Calendarprovides Google Calendar integration with timezone normalization and automatic paging; it assumes locally stored OAuth credentials
→ Read the full tutorial for a complete guide to the DSL
→ API Reference for detailed function signatures and parameters
MIT License - see LICENSE file for details.