workclock
Fast, correct business-time arithmetic (working hours, SLAs, shifts) for Python, with a Rust core — a maintained replacement for businesstimedelta, with a compatibility layer for existing code.
Status: pre-1.0; the API may still change in a minor release.
Install
pip install workclock
uv add workclock
Requires CPython 3.9 or later. The wheels use the stable ABI (abi3), one per platform: Linux x86_64 and aarch64 (manylinux and musllinux), macOS x86_64 and arm64, and Windows x64. Free-threaded CPython cannot load abi3 wheels. Other platforms build from the sdist, which needs a Rust toolchain.
The wheels bundle the IANA time zone database. They use the system database
when there is one, and the bundled copy otherwise, so they need no tzdata
package.
Quickstart
A support desk in London works 09:00–17:00 on weekdays with a lunch hour, and is closed on 25 and 28 December 2026. A ticket opened at 16:30 on Christmas Eve with an 8-hour SLA is due at 09:30 on 30 December.
from datetime import date, datetime, time
from zoneinfo import ZoneInfo
from workclock import Break, Holidays, Schedule, WorkDay
london = ZoneInfo("Europe/London")
support = Schedule(
[
WorkDay(time(9), time(17)), # Monday to Friday by default
Break(time(12), time(13)),
Holidays([date(2026, 12, 25), date(2026, 12, 28)]),
],
time_zone=london,
)
opened = datetime(2026, 12, 24, 16, 30, tzinfo=london)
support.add(opened, hours=8) # 2026-12-30 09:30, London time
support.between(opened, datetime(2026, 12, 29, 11, tzinfo=london)) # 2:30:00
support.next_period(opened) # (16:30, 17:00) on 24 December
Datetimes must be timezone-aware, and results keep the tzinfo you pass in.
Zones can be IANA names, zoneinfo.ZoneInfo, pytz zones or
datetime.timezone.utc. Errors subclass workclock.WorkclockError and the
matching built-in exception.
Batches and NumPy
between_many, add_many and sub_many take lists of datetimes, or NumPy
datetime64 and timedelta64 arrays, and process them in one call:
from datetime import datetime, time, timedelta, timezone
from workclock import Schedule, WorkDay
office = Schedule([WorkDay(time(9), time(17))])
tickets = [datetime(2026, 1, 5, 16, tzinfo=timezone.utc)] * 3
office.add_many(tickets, timedelta(hours=2)) # Tuesday 10:00, three times
Coming from businesstimedelta
Most code written for businesstimedelta runs unchanged on workclock's engine:
import datetime
import workclock.compat as businesstimedelta
rule = businesstimedelta.WorkDayRule(datetime.time(9), datetime.time(17))
friday = datetime.datetime(2026, 1, 9, 16)
friday + businesstimedelta.BusinessTimeDelta(rule, hours=2) # Monday 10:00 UTC
MIGRATING.md lists where it differs.
Why workclock
Correct
businesstimedelta 1.0.1 has had no release since 2019. workclock fixes:
- Overnight shifts. A 22:00–06:00 shift counts 0 hours between Monday 22:00 and Tuesday 06:00, and adding 4 hours at Monday 22:00 lands on Wednesday. workclock gives 8 hours and Tuesday 02:00.
- Crashes past the last holiday
(#8,
#10):
TypeErroronce a query passes the last date in a holiday list. - Infinite loops (#9) when holidays and working hours use different time zones. Every workclock search is bounded.
- Signed differences (#12):
Schedule.between(a, b)is negative whenbis beforea. - DST. Working hours are wall-clock times in each rule's IANA zone, and ambiguous or skipped times resolve the way RFC 5545 and JavaScript Temporal do.
Every rule is specified with examples in DESIGN.md, and the package is checked against a brute-force oracle with property-based tests.
Fast
Medians from a GitHub Actions run on a GitHub-hosted Ubuntu runner (AMD EPYC 7763, 4 cores):
| Case | businesstimedelta 1.0.1 | workclock | Speed-up |
|---|---|---|---|
| One call, a 5½-hour span | 167 µs | 3.89 µs | 43× |
| One call, a year with bank holidays | 50.8 ms | 85 µs | 600× |
| 1,000,000 pairs | 9.86 min | 2.11 s (between_many on NumPy arrays) |
280× |
The same single call takes 6.78 µs through workclock.compat. Runner CPUs
vary, so the times move from run to run; the speed-ups, measured within one
run, carry over better.
benchmarks/RESULTS.md
has every case with p95s.
Also for Rust and JavaScript
The same engine ships as the workclock
crate and the workclock npm
package, with the same results in every language.
Prior art & credits
workclock ports the test suite and the API of
businesstimedelta by
Sebastiaan Boer, released under the MIT license. The ported API is
workclock.compat, and the ported tests run against it in CI. The original
copyright notice is kept in
NOTICE.
License
Metadata
Release files for workclock 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| workclock-0.1.2.tar.gz | 47.1 kB | With provenance | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| workclock-0.1.2-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| workclock-0.1.2-cp39-abi3-musllinux_1_2_x86_64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ x86-64 | Details |
| workclock-0.1.2-cp39-abi3-musllinux_1_2_aarch64.whl | CPython 3.9 | abi3 | Linux musl 1.2+ ARM64 | Details |
| workclock-0.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| workclock-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| workclock-0.1.2-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| workclock-0.1.2-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 4.2 MB
Release files / workclock-0.1.2.tar.gz
| Download URL | workclock-0.1.2.tar.gz |
|---|---|
| Size | 47.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d09a3941122e8a093f40dd3e5ebf37b39d57eeba61b9b7843b41b93ef143ec0d
|
|
BLAKE2b-256 checksum How to use checksums |
5c93ffe65e7bcd4d13ffdd20396ad1bd06e8a858b22432ea5df114708907e963
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-win_amd64.whl
| Download URL | workclock-0.1.2-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 411.5 kB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
2a58728271706d8f51d50aa1856b133aad1148fa0d0b78606a8ac9c63e601c25
|
|
BLAKE2b-256 checksum How to use checksums |
3e5cf75f170cdec01fc3133ef3f01c87dfde7f14edc1b8229a32d3b33d30fd6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-musllinux_1_2_x86_64.whl
| Download URL | workclock-0.1.2-cp39-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 782.4 kB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
d46b4018792c0aaed67243cd072f1082c1abcc67286df8fc97cc1a96f8350310
|
|
BLAKE2b-256 checksum How to use checksums |
a4c1102a10b32ab98e11bb6b20fe3e6c6918a1d246933838c7f84341e2645125
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-musllinux_1_2_aarch64.whl
| Download URL | workclock-0.1.2-cp39-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 758.8 kB |
| Tags | CPython 3.9 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
568e11bc8cdf67099d25937621708996b317c838be11c79f1e1659f4ad370419
|
|
BLAKE2b-256 checksum How to use checksums |
c061d72a9af574ea7a300482074bc31bccc8b20dd55d5ff0cbcd234856fe31ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | workclock-0.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 576.7 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
b71362f5f3d648c8695e996d47936a807b1218ef7d881b0d2e4b33a16adc64f8
|
|
BLAKE2b-256 checksum How to use checksums |
2b615531790fcc2463b5b7f2603bdffef84bad30786233a775783e470f140682
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | workclock-0.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 580.1 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
454573789a800d6cdb5c2b50ffc7476bf75554da2d1bbc6d2696af7e8ad8a78e
|
|
BLAKE2b-256 checksum How to use checksums |
a06ea769337dc13fbfe684b7dfb91e99e709283753a9fa675144fba4f6a367ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | workclock-0.1.2-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 528.3 kB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
604bb36e2211e506af8083c059e7d0fd8d2a90afe2e51b2e9ea0131e4892f576
|
|
BLAKE2b-256 checksum How to use checksums |
5125e9913fae15edcb2786f784217e58c9c451e8428ea582e9e4e1024aa25a39
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logRelease files / workclock-0.1.2-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | workclock-0.1.2-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 517.9 kB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
3ddccccf16f70ac7d21b1272c655be3e9780fdb5a6c7e6e04e951eae6a14a153
|
|
BLAKE2b-256 checksum How to use checksums |
d406d2026d47d3eab6e7743e7f3086ff6fc5700baf04ff1d3c866a04d745dfa1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 26, 2026.
Transparency logSecurity for workclock 0.1.2
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.
Full provenance
All 8 files in this release have been uploaded with provenance metadata.
Attestation for workclock-0.1.2.tar.gz
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 Oct 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.