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: pre-1.0; the API may still change in a minor release.

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

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.1
File Size Uploaded Provenance
workclock-0.1.1.tar.gz 46.6 kB With provenance Details

Built distributions (wheels)

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

Download URL workclock-0.1.1.tar.gz
Size 46.6 kB
Tags Source
SHA-256 checksum
How to use checksums
349be14e6fb2c1de55d1310d45379b47bb3e1cae551c55651f7e8b0549b6d547
BLAKE2b-256 checksum
How to use checksums
9acd43dd47fe53f1695907afe3984fce3b2f73bbb29ee904793da97b58e80dcc
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.1-cp39-abi3-win_amd64.whl

Download URL workclock-0.1.1-cp39-abi3-win_amd64.whl
Size 412.3 kB
Tags CPython 3.9 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
7c568b081c9bf6446032bb33f02ef65388a7b33be6d3357224d3e6d327fd7ed7
BLAKE2b-256 checksum
How to use checksums
cdf072a2fd42a0862ed4209c10f310bd443cb66dea5d3ace532274d2e47c7cf4
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.1-cp39-abi3-musllinux_1_2_x86_64.whl

Download URL workclock-0.1.1-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
038c48f13577ed7fcc7e8ea600333d3aac5f1ac0605fba2885cd5b663cf616b9
BLAKE2b-256 checksum
How to use checksums
cc386cc93be6b895e50befdad5abd6e066734fdbdba10b676107047791d91ead
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.1-cp39-abi3-musllinux_1_2_aarch64.whl

Download URL workclock-0.1.1-cp39-abi3-musllinux_1_2_aarch64.whl
Size 759.5 kB
Tags CPython 3.9 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
fb88e452855d9ffee933978ce11555ced8c5392e6c45764aebf535f212778733
BLAKE2b-256 checksum
How to use checksums
de502eeadef40332ff0f03a26efe7f701b5fed94473487d9585848ab2638d514
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.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL workclock-0.1.1-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
c238020b7ea9bae6e6909d083f0c83b9a8b773dbb8fb52ce64b9c4a1c57ce906
BLAKE2b-256 checksum
How to use checksums
989aa7ec3dc3f6cfb4e6f15b4a370fe03ec90aaabec87b174c95a6e24b4cf25a
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.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL workclock-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 580.8 kB
Tags CPython 3.9 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
cb3285fb23d2f4ef821934ded335b7501a42540fb6705065ceddc67869f91b0d
BLAKE2b-256 checksum
How to use checksums
ad5ceb7f4aaeee638e7c338385b9f40e5b8a0a1d14a75d15946bee24bbb0f653
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.1-cp39-abi3-macosx_11_0_arm64.whl

Download URL workclock-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
Size 529.0 kB
Tags CPython 3.9 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
cf19f310535ae7d326f29dcf40f3b657574a509efb08b60e0c42991fb0c32713
BLAKE2b-256 checksum
How to use checksums
23c885d91830d99ad5d7b08b97034ac36d1a2073bf8d26d5ed97c8e0516c1f8a
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.1-cp39-abi3-macosx_10_12_x86_64.whl

Download URL workclock-0.1.1-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
7a5645be24ca4d0a0a84013739d0f93d9e8bb05f4895d4d10eda926002ecb65a
BLAKE2b-256 checksum
How to use checksums
494931ebf2ff9aa04d5a4ce01409d82b070b82ad36714765bd4409c8d9045611
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.1

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