Skip to main content

⏳ Allen

A modern, type-safe Python library for modeling, normalizing, and manipulating time periods using Allen's Interval Algebra.

PyPI version Python Version Checked with Pyright Code style: Ruff License: MIT


📌 Overview

Working with complex time ranges, schedules, overlapping events, and gaps in Python can easily lead to spaghetti code full of nested if/else checks.

allen provides high-performance, strictly typed primitives (Period and PeriodSet) that implement Allen's Interval Algebra relations (such as before, meets, overlaps, starts, during, finishes, and equals).

It abstracts away edge-case complexity by automatically merging overlapping or contiguous intervals, calculating interior gaps, computing intersections, and performing set arithmetic (+, -, &, |) in $O (N \log N)$ or linear $O (N + M)$ time.


✨ Key Features

  • Zero external dependencies: Lightweight and fast.
  • Strictly Typed & PEP 561 Compliant: Built with py.typed and zero-suppression typing for Pyright/Mypy strict modes.
  • Automatic Normalization: PeriodSet keeps intervals sorted, non-overlapping, and disjoint by design.
  • Intuitive Set Algebra: Overloaded Python operators (+, -, &, |) for declarative time manipulations.
  • Immutability & Safety: Memory-efficient slots-based dataclasses preventing accidental side effects.

📦 Installation

Install allen using uv or pip:

# Using uv (Recommended)
uv add allen-python

# Using pip
pip install allen-python

🚀 Quickstart & Real-World Examples

1. Merging Overlapping Shifts (Scheduler Normalization)

Use Case: You receive raw shift records from multiple employees or systems. Some shifts overlap or are contiguous. You want to compute the total actual working hours without double-counting overlapping times.

from datetime import datetime, timezone
from allen import Period, PeriodSet

# Raw shift logs (some overlapping, some contiguous)
shift1 = Period(
    datetime(2026, 10, 1, 8, 0, tzinfo=timezone.utc),
    datetime(2026, 10, 1, 12, 0, tzinfo=timezone.utc)
)
shift2 = Period(
    datetime(2026, 10, 1, 11, 0, tzinfo=timezone.utc),
    datetime(2026, 10, 1, 15, 0, tzinfo=timezone.utc)
)  # Overlaps shift1
shift3 = Period(
    datetime(2026, 10, 1, 16, 0, tzinfo=timezone.utc),
    datetime(2026, 10, 1, 18, 0, tzinfo=timezone.utc)
)  # Disjoint shift

# Creating a PeriodSet automatically normalizes and merges overlaps
work_day = PeriodSet([shift1, shift2, shift3])

print(f"Normalized periods: {len(work_day)}")  # Output: 2
print(f"Total worked hours: {work_day.duration}")  # Output: 9:00:00

2. Finding Available Slot Windows (Calendar Free/Busy Analysis)

Use Case: You are building a booking engine. You know a provider's total working window (hull) and their booked appointments (PeriodSet). You need to find all available idle windows (gaps).

from datetime import datetime, timezone
from allen import Period, PeriodSet

# Appointments booked throughout the day
appointments = PeriodSet([
    Period(
        datetime(2026, 10, 1, 9, 0, tzinfo=timezone.utc),
        datetime(2026, 10, 1, 10, 30, tzinfo=timezone.utc)
    ),
    Period(
        datetime(2026, 10, 1, 11, 0, tzinfo=timezone.utc),
        datetime(2026, 10, 1, 12, 0, tzinfo=timezone.utc)
    ),
    Period(
        datetime(2026, 10, 1, 14, 0, tzinfo=timezone.utc),
        datetime(2026, 10, 1, 15, 30, tzinfo=timezone.utc)
    ),
])

# Extract the idle gaps between scheduled appointments
free_slots = appointments.gaps

for slot in free_slots:
    print(f"Available break: {slot.start.strftime('%H:%M')} -> {slot.end.strftime('%H:%M')}")

# Output:
# Available break: 10:30 -> 11:00
# Available break: 12:00 -> 14:00

3. Subtracting Maintenance Windows (Uptime Calculation)

Use Case: You monitor server uptime across a month, but you must exclude scheduled maintenance windows (PeriodSet - PeriodSet or PeriodSet - Period) to calculate SLA compliance.

from datetime import datetime, timezone
from allen import Period, PeriodSet

# Total active uptime monitor
active_window = PeriodSet([
    Period(
        datetime(2026, 10, 1, 0, 0, tzinfo=timezone.utc),
        datetime(2026, 10, 1, 23, 59, tzinfo=timezone.utc)
    )
])

# Maintenance windows to exclude
maintenance = Period(
    datetime(2026, 10, 1, 13, 0, tzinfo=timezone.utc),
    datetime(2026, 10, 1, 15, 0, tzinfo=timezone.utc)
)

# Perform set difference
billable_uptime = active_window - maintenance

print(f"Remaining disjoint periods: {len(billable_uptime)}")  # Output: 2
print(f"Net operational duration: {billable_uptime.duration}")  # Output: 21:59:00

🛠️ API Reference Summary

Period

Primitive representing a single timezone-aware half-open interval $[start, end)$. All datetime objects must be timezone-aware (e.g., tzinfo=timezone.utc).

  • Instantiation: Period(start: datetime, end: datetime)
  • Key Methods:
    • .intersect(other: Period) -> Period | None: Returns the overlapping period or None.
    • .union(other: Period) -> Period | None: Merges contiguous or overlapping periods into one.
  • Operators:
    • period_a - period_b -> list[Period]: Set difference between two single periods.
    • dt in period: Returns True if dt falls within $[start, end)$.

PeriodSet

Normalizing collection that automatically keeps a sequence of Period instances sorted, non-overlapping, and disjoint.

  • Instantiation: PeriodSet(periods: list[Period] = [])
  • Operators:
    • + / |: Union of sets/periods (PeriodSet + PeriodSet).
    • -: Set difference (PeriodSet - PeriodSet or PeriodSet - Period).
    • &: Set intersection (PeriodSet & PeriodSet or PeriodSet & Period).
    • dt in period_set: Checks if a datetime or Period is contained.
  • Properties:
    • .duration -> timedelta: Sum of active durations across all disjoint periods.
    • .gaps -> PeriodSet: Returns a new PeriodSet representing the interior gaps between periods.
    • .hull -> Period | None: Returns a single Period spanning from global .first.start to .last.end.
    • .first -> Period | None: Returns the earliest period in the set.
    • .last -> Period | None: Returns the latest period in the set.
    • .is_empty -> bool: Returns True if the set contains no periods.

📄 License

Distributed under the MIT License. See LICENSE for more details.

Release files for allen-python 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for allen-python 0.1.0
File Size Uploaded
allen_python-0.1.0.tar.gz 8.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for allen-python 0.1.0
File Interpreter ABI Platform
allen_python-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 18.4 kB

Release files / allen_python-0.1.0.tar.gz

Download URL allen_python-0.1.0.tar.gz
Size 8.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6ea6e58c4be76dd1aae116cbc63cd1a2174b3afbed42a007a67ab69e159866ef
BLAKE2b-256 checksum
How to use checksums
4757f06edb009c0ec9ac1417078e859277e22f883aa10f3d58c52e40e4f8039b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / allen_python-0.1.0-py3-none-any.whl

Download URL allen_python-0.1.0-py3-none-any.whl
Size 9.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
85df4b0f68a28f9d63086cf7f53ee17bf787446b9c869994d07bb52003ae118c
BLAKE2b-256 checksum
How to use checksums
c213161a0ff26ef642c03d0269efb7f04019168350e1ff69de2b99d29420b297
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page