Skip to content

Install

Add workclock to a Rust, Python or JavaScript project, with the toolchain and time zone data each one needs.

This page shows how to add workclock to a Rust, Python or JavaScript project, which language versions it supports, and where each package gets its time zone data.

Add the package

cargo add workclock jiff

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. The package is WebAssembly with TypeScript types included, so it needs no Rust toolchain. Browsers load it through a bundler, or without one from workclock/web.

Browsers without a bundler

In JavaScript, import from workclock/web and call its default export once before using anything else. init() fetches workclock_bg.wasm from the package's pkg/web/ directory, so serve that directory with your page. With a bundler, or in Node.js, import from workclock instead.

import init, { Schedule } from "workclock/web";

await init();

const support = new Schedule({
  timeZone: "Europe/London",
  rules: [{ type: "workday", start: "09:00", end: "17:00" }],
});

Time zone data

Working hours are wall-clock times in an IANA time zone, so every binding needs the IANA time zone database.

  • JavaScript: the npm package bundles the database and always uses its bundled copy, whose zone rules are as current as the workclock release.
  • Python: the wheels bundle the database too. They use the system database when there is one, and the bundled copy otherwise.
  • Rust: the 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 in Rust, even "UTC", is an unknown-zone error (workclock::Error::UnknownZone). Install the tzdata package, point TZDIR at a zoneinfo directory, or enable jiff's tzdb-bundle-always feature to bundle the database:

cargo add jiff --features tzdb-bundle-always

The Python samples on this site build their datetimes with the standard library's zoneinfo.ZoneInfo, which is separate from workclock's copy. On Windows, which has no system database, ZoneInfo("Europe/London") needs the tzdata package from PyPI (zoneinfo: data sources):

pip install tzdata

Results can differ between machines only when their databases disagree about a zone's history or future. Time zones and DST explains how workclock uses the zones.

Prompt for your coding agent

Paste this into your coding agent before asking it to use workclock. It covers the install commands, the rules each language follows and an example to check the result against.

Add the workclock library to this project to compute working time: working hours, SLAs and shifts, with breaks, holidays and time zones.

Install:
- Rust: cargo add workclock jiff (the API takes and returns jiff 0.2 types)
- Python: pip install workclock, or uv add workclock
- JavaScript or TypeScript: npm i workclock
Requirements: Rust 1.85 or later, CPython 3.9 or later, Node.js 22 or later.

How it works:
- Build one Schedule from rules: WorkDay (working hours on given weekdays), Break (time off on given weekdays) and Holidays (dates removed from 00:00 to the next 00:00).
- Give the schedule an IANA time zone name, such as "Europe/London". Rules without their own zone use the schedule's, holidays included.
- WorkDay defaults to Monday to Friday. An end time at or before the start time is an overnight shift.
- between(a, b) returns the working time between two instants. It is negative when b is before a.
- add(t, d) returns the earliest instant with exactly d of working time after t; sub(t, d) goes backwards. add(t, 0) returns t unchanged.
- next_period and previous_period (nextPeriod and previousPeriod in JavaScript) return a stretch of working time.

Language rules:
- Python: from workclock import Schedule, WorkDay, Break, Holidays. Pass datetimes with tzinfo (zoneinfo.ZoneInfo); naive datetimes raise ValueError. Weekdays are integers, 0 is Monday. Durations are timedelta, or add(t, hours=8). For many inputs, use between_many, add_many and sub_many.
- JavaScript: import { Schedule } from "workclock". new Schedule({ timeZone, rules: [{ type: "workday", start: "09:00", end: "17:00" }, ...] }). Instants are Date objects or ISO 8601 strings with an offset. Durations are numbers of milliseconds. Weekdays are "mon" to "sun". For many inputs, use betweenMany, addMany and subMany. In a browser without a bundler, import init from "workclock/web" and await init() first.
- Rust: use workclock::{Schedule, WorkDay, Break, Holidays, Weekdays}; build with Schedule::builder().time_zone("...").rule(...).build()?. Instants are jiff::Zoned or jiff::Timestamp; durations are jiff::SignedDuration.

Errors: a schedule with no working time fails when it is built (NoWorkingTimeError in Python and JavaScript, workclock::Error::NoWorkingTime in Rust). Every search is bounded, so no call hangs. In Python and JavaScript the errors subclass WorkclockError.

Existing businesstimedelta code: replace its import with "import workclock.compat as businesstimedelta". Most code runs unchanged.

Check your code with this example before using it: a support desk in Europe/London works 09:00 to 17:00, Monday to Friday, with a break from 12:00 to 13:00, and is closed on 2026-12-25 and 2026-12-28. Adding 8 working hours to 2026-12-24 16:30 London time gives 2026-12-30 09:30 London time.

Full documentation for agents: https://workclock.karthik.nz/llms-full.txt

Next steps

Write a first program in your language: Quickstart: Rust, Quickstart: Python or Quickstart: JavaScript.

Report a problem with this pageAsk a question in Discussions

On this page