⏳ Allen
A modern, type-safe Python library for modeling, normalizing, and manipulating time periods using Allen's Interval Algebra.
📌 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.typedand zero-suppression typing for Pyright/Mypy strict modes. - Automatic Normalization:
PeriodSetkeeps 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 orNone..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: ReturnsTrueifdtfalls 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 - PeriodSetorPeriodSet - Period).&: Set intersection (PeriodSet & PeriodSetorPeriodSet & Period).dt in period_set: Checks if adatetimeorPeriodis contained.
- Properties:
.duration -> timedelta: Sum of active durations across all disjoint periods..gaps -> PeriodSet: Returns a newPeriodSetrepresenting the interior gaps between periods..hull -> Period | None: Returns a singlePeriodspanning from global.first.startto.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: ReturnsTrueif 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)
| File | Size | Uploaded | |
|---|---|---|---|
| allen_python-0.1.0.tar.gz | 8.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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