Skip to main content

workclock

Fast, correct business-time arithmetic (working hours, SLAs, shifts) for Rust, Python and JavaScript — a maintained replacement for businesstimedelta, with a compatibility layer for existing code.

Status: in development. No release has been published yet.

Install

cargo add workclock jiff
pip install workclock
uv add workclock
npm i workclock

The Rust API takes and returns jiff 0.2 types, so Rust code depends on jiff too.

Requirements

  • Rust: 1.85 or later.
  • Python: 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.
  • JavaScript: Node.js 22 or later. Browsers load the package through a bundler, or without one from workclock/web.

Time zone data

The Python wheels and the npm package bundle the IANA time zone database, so they need no system tzdata. The npm package always uses its bundled copy, whose zone rules are as current as the workclock release. The Python wheels use the system database when there is one, and the bundled copy otherwise.

The Rust crate uses jiff's defaults: the system database (/usr/share/zoneinfo, or the directory in TZDIR) on Linux and macOS, and a bundled copy on Windows. On a Linux image without tzdata, as some slim and distroless images are, every named zone, even "UTC", is an unknown-zone error. Install the tzdata package, point TZDIR at a zoneinfo directory, or enable jiff's tzdb-bundle-always feature to bundle the database.

Use cases

  • Support-ticket SLAs: when is a ticket due, and how much of its SLA has elapsed?
  • Payroll and overtime: how many hours of a shift fell inside contracted time?
  • Staff rosters, including overnight shifts.
  • Delivery ETAs that only count operating hours.

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.

Rust

use jiff::civil::{date, time};
use jiff::{SignedDuration, Zoned};
use workclock::{Break, Holidays, Schedule, Weekdays, WorkDay};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let support = Schedule::builder()
        .time_zone("Europe/London")
        .rule(WorkDay::new(time(9, 0, 0, 0), time(17, 0, 0, 0), Weekdays::MON_TO_FRI))
        .rule(Break::new(time(12, 0, 0, 0), time(13, 0, 0, 0), Weekdays::MON_TO_FRI))
        .rule(Holidays::new([date(2026, 12, 25), date(2026, 12, 28)]))
        .build()?;

    let opened: Zoned = "2026-12-24T16:30[Europe/London]".parse()?;
    let due = support.add(&opened, SignedDuration::from_hours(8))?;
    assert_eq!(due.to_string(), "2026-12-30T09:30:00Z");

    let now: Zoned = "2026-12-29T11:00[Europe/London]".parse()?;
    assert_eq!(support.between(&opened, &now), SignedDuration::from_mins(150));
    Ok(())
}

API documentation is on docs.rs.

Python

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

between_many, add_many and sub_many take lists or NumPy arrays and process them in one call. Most code written for businesstimedelta runs unchanged with import workclock.compat as businesstimedelta; MIGRATING.md lists where it differs.

JavaScript

import { Schedule } from "workclock";

const support = new Schedule({
  timeZone: "Europe/London",
  rules: [
    { type: "workday", start: "09:00", end: "17:00" },
    { type: "break", start: "12:00", end: "13:00" },
    { type: "holidays", dates: ["2026-12-25", "2026-12-28"] },
  ],
});

support.add("2026-12-24T16:30:00Z", 8 * 3_600_000); // Date 2026-12-30T09:30:00.000Z
support.between("2026-12-24T16:30:00Z", new Date("2026-12-29T11:00:00Z")); // 9000000 ms

Instants are Dates or ISO 8601 strings with an offset; durations are milliseconds. In browsers without a bundler, import the default init from workclock/web and await init() first.

More complete programs are in crates/workclock/examples, examples/python and js/examples. The errors each binding raises are listed in DESIGN.md.

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): TypeError once 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 when b is before a.
  • DST. Working hours are wall-clock times in each rule's IANA zone, and ambiguous or skipped times resolve the way jiff, RFC 5545 and JavaScript Temporal do.

Every rule is specified with examples in DESIGN.md, and each binding is checked against a brute-force oracle with property-based tests.

Fast

Medians on a 2017 laptop (Intel i5-8250U, Windows 11), from Python:

Case businesstimedelta 1.0.1 workclock Speed-up
One call, a 5½-hour span 385 µs 8.5 µs 45×
One call, a year with bank holidays 134 ms 163 µs 820×
1,000,000 pairs 22.5 min 2.92 s (between_many on NumPy arrays) 460×

The same single call takes 1.7 µs from Rust and 2.2 µs from JavaScript, and 16 µs through workclock.compat, which keeps the schedule it builds from businesstimedelta's mutable rules until their attributes change. benchmarks/RESULTS.md has every case with p95s, and DESIGN.md explains where the time goes.

Prior art & credits

workclock ports the test suite and the API of businesstimedelta by Sebastiaan Boer, released under the MIT license. Its last release, 1.0.1, was in September 2019, and issues #3, #8, #9, #10 and #12 remain open. The ported API is workclock.compat, and the ported tests run against it in CI. The original copyright notice is kept in NOTICE.

License

MIT

Metadata

Release files for workclock 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 workclock 0.1.0
File Size Uploaded Provenance
workclock-0.1.0.tar.gz 46.7 kB With provenance Details

Built distributions (wheels)

Table of built distributions (wheels) for workclock 0.1.0
File
workclock-0.1.0-cp39-abi3-win_amd64.whl CPython 3.9 abi3 Windows x86-64 Details
workclock-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl CPython 3.9 abi3 Linux musl 1.2+ x86-64 Details
workclock-0.1.0-cp39-abi3-musllinux_1_2_aarch64.whl CPython 3.9 abi3 Linux musl 1.2+ ARM64 Details
workclock-0.1.0-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.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.9 abi3 Linux glibc 2.17+ ARM64 Details
workclock-0.1.0-cp39-abi3-macosx_11_0_arm64.whl CPython 3.9 abi3 macOS 11.0+ ARM64 Details
workclock-0.1.0-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.0.tar.gz

Download URL workclock-0.1.0.tar.gz
Size 46.7 kB
Tags Source
SHA-256 checksum
How to use checksums
2f5e451bb9654a68ac96eebc7133bc4bb05eda4aa5e518f04711dec9b02e8949
BLAKE2b-256 checksum
How to use checksums
72a60d7c716043f97ea0f551ca31d94b02d1f1d659a352e28446565ff308f790
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-win_amd64.whl

Download URL workclock-0.1.0-cp39-abi3-win_amd64.whl
Size 412.4 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
8cd74f7313be5fef2b738112e54d775f45f344f21a46850d479813ef7afb0582
BLAKE2b-256 checksum
How to use checksums
e21153da4ae978b1ea06fbf721cdfca442524eacc72a11b840da18378685d2aa
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL workclock-0.1.0-cp39-abi3-musllinux_1_2_x86_64.whl
Size 783.1 kB
Tags CPython 3.9 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
2d254c55226e92d05d0675132719a9526d878c28da2f65a1c04fc88452edf5fe
BLAKE2b-256 checksum
How to use checksums
9f9907b44eb6f5a0cdb804008319737fbce5eae6d8d93433360f2085e314c68b
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-musllinux_1_2_aarch64.whl

Download URL workclock-0.1.0-cp39-abi3-musllinux_1_2_aarch64.whl
Size 759.7 kB
Tags CPython 3.9 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
60b3d5ea6ec069df022125612f838b8a63f5e8f1647ab653f2cf90649eed5c34
BLAKE2b-256 checksum
How to use checksums
aec6cbdafb101be71295c31b96106dc4e7463515626f957c7d54159455c6f8fe
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL workclock-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 577.6 kB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
fcc0444bcd4fc479b964941c4c241c9582411f967d8bc2ef479bf2a5785b4a1c
BLAKE2b-256 checksum
How to use checksums
82d28a75a755c0c84831096888dc7ca832da5b71d188bf24ae8252a649d5973e
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL workclock-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 581.2 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
36fba7cb01b2145d54a1e80e5eaa84c692d9db4eb4173a806daa7cfe5a85879d
BLAKE2b-256 checksum
How to use checksums
e6d89402d21e70af2eb9c85c909e36a9c1ef8c397aaf301c1177b74584c10b4b
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-macosx_11_0_arm64.whl

Download URL workclock-0.1.0-cp39-abi3-macosx_11_0_arm64.whl
Size 529.2 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
5bd5f668e4525c36431a6b636ec02865e166f0c10b739cd31328bcb2faafbd03
BLAKE2b-256 checksum
How to use checksums
3e2c2c0a7bbc777608ecc74f736c95113027bd1891ea9dd66f4883683fd1fb32
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 25, 2026.

Transparency log

Release files / workclock-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl

Download URL workclock-0.1.0-cp39-abi3-macosx_10_12_x86_64.whl
Size 518.7 kB
Tags CPython 3.9 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
dbfcbbe1f28f34ceadccb807b270960565ab0faadb1b429a6b5d955eb9df595b
BLAKE2b-256 checksum
How to use checksums
fefa4d51ec0d603fd5d4bdf44fd58b0d3c3d5f6f85ec9490bb83b76ea3039698
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 25, 2026.

Transparency log

Security for workclock 0.1.0

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.

Select from 8 files

Attestation for workclock-0.1.0.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 25, 2026.

Transparency log

A 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.

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