Skip to main content

Business-hours, availability, downtime, capacity, and SLA working-time calculations for Python, powered by Rust.

Project description

TimeGrid for Python

PyPI Version License Runtime

Business-hours, availability, downtime, capacity, and SLA working-time calculations for Python, powered by Rust.

TimeGrid turns schedule rules into a queryable operational timeline. Define working hours, lunch breaks, holidays, maintenance windows, capacity overrides, and named schedule entries; serialize the source definition as JSON; compile it once; then answer timestamp and range queries quickly.

Use it for factory calendars, machine availability, SLA clocks, business-hour math, capacity-aware slot search, and systems that need a dependable time layer before they schedule, simulate, dispatch, or report.

Not a scheduler. Not a simulator. Not a timezone database. TimeGrid is the availability and working-time engine those systems can depend on.

Install

pip install timegrid

For supported platforms, pip automatically selects the matching prebuilt wheel from PyPI.

Operating system CPU architecture Python Wheel support
Windows x86_64 / AMD64 CPython 3.9+ Prebuilt win_amd64 wheel
Linux x86_64 / AMD64 CPython 3.9+ Prebuilt manylinux2014_x86_64 wheel
macOS Intel x86_64 and Apple Silicon arm64 CPython 3.9+ Prebuilt universal2 wheel

The wheels use Python's stable ABI (abi3), so one wheel per OS/architecture supports CPython 3.9 and newer. Other platforms or architectures can still install from the source distribution when a Rust toolchain is available.

30-Second Example

Calculate an SLA due date while skipping lunch, nights, weekends, and one-off closures:

from datetime import date, datetime, time
from timegrid import TimeGridCalendar, hours

calendar = (
    TimeGridCalendar.weekdays(time(9), time(17))
    .break_weekdays(time(12), time(13))
    .close(date(2026, 1, 1))
    .set_closed_window("company-offsite", datetime(2026, 1, 7, 13), datetime(2026, 1, 7, 17))
)

opened_at = datetime(2026, 1, 5, 11)
trace = calendar.at(opened_at).trace_work_duration(hours(10))

print(trace.result)
for step in trace.steps:
    print(step.window, step.duration)

Practical Examples

Runnable examples live in examples/:

python examples/sla_due_date.py
python examples/manufacturing_capacity_slot.py
python examples/fleet_batch_snapshot.py
python examples/json_schedule_template.py
python examples/overnight_shift.py

From a local checkout:

.\.venv\Scripts\python.exe examples\run_all.py
Example What it shows
sla_due_date.py Business-hour due dates and consumed working windows.
manufacturing_capacity_slot.py Downtime, capacity boosts, and first slot with enough capacity.
fleet_batch_snapshot.py Batch capacity and analysis queries across many compiled machine timelines.
json_schedule_template.py JSON schedule templates plus per-machine exceptions.
overnight_shift.py Open rules that cross midnight and breaks inside overnight shifts.

What It Answers

Question API
Can this machine, team, or queue work now? timeline.analyze(now).can_work
What is the effective capacity at this timestamp? analysis.capacity
Which state window contains this timestamp? analysis.current_window
When does the state change next? analysis.next_transition
Why is this timestamp blocked or boosted? calendar.at(now).analyze().matches
How much usable business time exists in a range? timeline.get_working_duration(start, end)
Where is the first continuous slot with enough capacity? timeline.find_first_slot(start, hours(2), 3)
What are many capacities at once? timeline.get_capacities_at(instants)
What is the state of many machine timelines now? TimeGridTimelineBatch(timelines).get_capacities_at(now)

Schedule Definitions

TimeGrid stores source rules, not compiled runtime state. That makes definitions easy to save, review, ship, and apply to many operational entities:

from datetime import datetime, time
from timegrid import TimeGridCalendar

template_json = (
    TimeGridCalendar.create()
    .open_weekdays(time(7), time(19))
    .break_weekdays(time(12), time(12, 45))
    .capacity(3)
    .to_json(indented=True)
)

template = TimeGridCalendar.from_json(template_json).to_definition()

machine = (
    template.to_calendar()
    .set_closed_window("machine-42-maintenance", datetime(2026, 1, 5, 15), datetime(2026, 1, 5, 17))
    .set_capacity_window("machine-42-overtime-crew", datetime(2026, 1, 5, 17), datetime(2026, 1, 5, 19), 5)
    .compile(datetime(2026, 1, 5), datetime(2026, 1, 6))
)

Compile For Repeated Reads

Use compile(start, end) when the same calendar will be queried many times. A compiled timeline is read-only and answers point and range queries by searching precomputed state segments.

from timegrid import TimeGridTimelineBatch, hours

timeline = calendar.compile(month_start, month_end)

point = timeline.analyze(now)
work = timeline.get_working_duration(day_start, day_end)
slot = timeline.find_first_slot(day_start, hours(4), 2)

capacities = timeline.get_capacities_at(instants)
analyses = timeline.analyze_many(instants)

fleet = TimeGridTimelineBatch(machine_timelines)
fleet_capacities = fleet.get_capacities_at(now)

API Style

Python code can use Python naming conventions while compatibility aliases preserve the original TimeGrid.NET-style names.

Surface Python style Compatibility style
Classes TimeGridCalendar, TimeWindow same
Methods and properties snake_case PascalCase
Constants DayOfWeek.MONDAY DayOfWeek.Monday

Why It Is Fast

Operation Runtime strategy
Point analysis Binary search over compiled state segments
Range analysis Binary search, then scan touched segments only
Transition lookup Segment boundary lookup
Working duration Sum usable segment durations
Slot search Scan continuous capacity-matching segments
Batch point queries One Python call, Rust loop over compiled timelines
JSON Rust serde source definition serialization

Local quick-query benchmark on CPython 3.12, Windows 10, Rust release wheel. The scenario matches the TimeGrid.NET QuickQueryPerf benchmark: 50,000 compiled state segments, 1,000 compiled machine timelines, 20,000 warmup calls, and 1,000,000 measured calls.

Common API, one Python call per query:

Scenario Operation Python/Rust TimeGrid.NET
50,000 compiled state segments get_capacity_at 0.153 us/query 0.052 us/query
50,000 compiled state segments analyze 0.225 us/query 0.064 us/query
1,000 compiled machine timelines get_capacity_at sweep 123.154 us 26.625 us
1,000 compiled machine timelines analyze sweep 189.991 us 34.463 us

Batch API, one Python call per batch:

Scenario Operation Python/Rust
50,000 compiled state segments get_capacities_at 0.076 us/query
50,000 compiled state segments analyze_many 0.220 us/query
1,000 compiled machine timelines TimeGridTimelineBatch.get_capacities_at sweep 28.342 us
1,000 compiled machine timelines TimeGridTimelineBatch.analyze sweep 118.398 us

Measurements exclude compile time and run against already compiled timelines.

Good Fit

Use TimeGrid for Use something else for
Business-hours and working-time calculations Calendar UI rendering
Manufacturing availability and machine downtime Queue dispatching
Capacity-aware slot search Optimization solving
SLA clocks and operational reporting Full timezone database behavior
JSON-backed schedule definitions General-purpose date-range value objects
Scheduler or simulator availability layer Running background jobs

Core Rules

  • Time windows are half-open: [start, end).
  • Capacity 0 means unavailable.
  • Capacity overrides split the timeline into state segments.
  • JSON stores source definitions, not compiled timelines.
  • Compiled timelines are read-only and optimized for repeated analysis.
  • Use naive datetime values consistently in your chosen local or UTC convention.

Direct APIs

calendar.can_work(now)
calendar.get_capacity_at(now)
calendar.get_windows_at(now)
calendar.get_previous_transition_time(now)
calendar.get_next_transition_time(now)
calendar.get_open_windows(start, end)
calendar.get_unavailable_windows(start, end)
calendar.get_state_windows(start, end)
calendar.get_working_duration(start, end)
calendar.find_first_slot(start, hours(2), 2)

timeline.get_capacities_at(instants)
timeline.analyze_many(instants)
TimeGridTimelineBatch(timelines).get_capacities_at(now)

Links

If TimeGrid gives you a wrong answer, a confusing error, or a missing API for a real scheduling or availability problem, please open a GitHub issue with the smallest calendar definition and query that reproduces it.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

timegrid-0.1.5-cp39-abi3-win_amd64.whl (375.9 kB view details)

Uploaded CPython 3.9+Windows x86-64

timegrid-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (546.8 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

timegrid-0.1.5-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (994.1 kB view details)

Uploaded CPython 3.9+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file timegrid-0.1.5-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: timegrid-0.1.5-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 375.9 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for timegrid-0.1.5-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 f17874c220fcc17aadc04c87539ad2943aa2df36b43b470bc393e6e29d4b42b5
MD5 0f4026033480517a3416184937890c37
BLAKE2b-256 9f7c4332d8754aee1b9001f2af66ecee8bcc6701a9789adc82821b68d0831f16

See more details on using hashes here.

File details

Details for the file timegrid-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for timegrid-0.1.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 c43683296cf7d1b80d296f1f5816d233a8dcfdcb6f85e8983f1d9a4d2eb956ca
MD5 87000c4c2c1b5987273f4d7663af0fd2
BLAKE2b-256 6336e665d09030b3bf2c6d35ce8a522261f5c30c9cae450efe0bf3911a313290

See more details on using hashes here.

File details

Details for the file timegrid-0.1.5-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for timegrid-0.1.5-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 7e6930cf4c0cb1b0486bd23c88e9c8345eaff73cf5a90b94672a0ef3cffe45e1
MD5 833cd313886ec80a76558447c800af19
BLAKE2b-256 900060a43a04baba372978b789b6dd3e09132fe3cd06bdffc61ff03fb73713a5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page