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.4-cp39-abi3-win_amd64.whl (376.0 kB view details)

Uploaded CPython 3.9+Windows x86-64

timegrid-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (546.7 kB view details)

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

timegrid-0.1.4-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.4-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: timegrid-0.1.4-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 376.0 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.4-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 9c49425b777ba462e680361aef82cb94006bcefa8e47b78238f39fab1cddeb60
MD5 e944d8d75e78d8ec61d024f3f6aa9a3a
BLAKE2b-256 c7c9113efed8de178f20a6043bcd34183f0dce31984f2fb59ca2b57fd3611474

See more details on using hashes here.

Provenance

The following attestation bundles were made for timegrid-0.1.4-cp39-abi3-win_amd64.whl:

Publisher: release.yml on code-gihan/timegrid-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for timegrid-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 04b8b0a254eb5113fb543b1ee7aad07323aa36e9ef9d2decb4ba032f6dc5f77e
MD5 6ce4961c598c69e8b02cd7994662e7a8
BLAKE2b-256 be6861e7dec1f84645ac8da7ff6d908e2e2eb413320fd5377871c20838536678

See more details on using hashes here.

Provenance

The following attestation bundles were made for timegrid-0.1.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on code-gihan/timegrid-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file timegrid-0.1.4-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.4-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 073f757285227f95164b92d4b5e29c0b634e0502fecf0e0a6fb91f846ee26260
MD5 ee5919f0e69f629743462484cb0b9d68
BLAKE2b-256 067de4621071ffafec9a0a662e10ad02995434ccd4ece432b69574a94a8726e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for timegrid-0.1.4-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: release.yml on code-gihan/timegrid-python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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