Skip to content

Releases: jamesgober/clock-lib

v1.0.0 — Stable API

21 May 09:45

Choose a tag to compare

v1.0.0 — Stable API

clock-lib reaches 1.0. The public surface, the performance, the documentation, and the supply-chain story are all settled. From this release forward, every change against this major version is non-breaking.

What clock-lib Is

A thin, well-inlined layer over std::time that gives you the two kinds of time every program needs — monotonic for measuring elapsed time, wall-clock for timestamps — as distinct types the compiler refuses to mix. A Clock trait makes time-driven code testable without thread::sleep. Zero runtime dependencies, no unsafe, no_std-compatible.

use clock_lib as clock;

// Measure how long something takes (never goes backwards).
let start = clock::now();
// ... do work ...
let took = clock::elapsed(start);

// Stamp an event (calendar time).
let secs = clock::unix();
[dependencies]
clock-lib = "1.0"

The Stable Surface

Everything below is committed for the 1.x line and will not change without a major version bump.

Tier-1 free functions

now, elapsed, wall, unix, unix_ms, unix_ns — one-line entry points for the common case.

Types

  • Monotonicnow, elapsed, duration_since, checked_duration_since, saturating_duration_since.
  • Wallnow, unix_seconds, unix_millis, unix_nanos. Pre-epoch system clocks saturate to 0; no panics, no silent wrapping.

Trait + implementations

  • ClockSend + Sync trait with now and wall methods. Blanket impls for Arc<C> and &C.
  • SystemClock — zero-sized, Copy, const-constructible production clock.
  • ManualClock — lock-free, atomic-offset test clock. new, advance, offset.

Constants

  • VERSION — the only item exposed without the std feature.

Performance, Verified

The performance document records the recorded median for every public reading alongside the raw std::time call it forwards to. The wrappers sit on top of the bare std functions inside criterion's noise band on Windows 11 / Rust 1.85.

Wrapper Δ vs raw std::time
clock_lib::now −0.2%
Monotonic::now −0.7%
SystemClock::now −0.9%
clock_lib::wall −0.7%
Wall::now −0.6%
clock_lib::elapsed −0.2%

ManualClock::now runs in 752 ps; ManualClock::advance(1ns) in 3.75 ns. A test that calls advance + now a million times spends about 4 ms on the clock.

Engineering Posture

  • #![forbid(unsafe_code)] at the crate root.
  • REPS-complete lint stack#![deny(warnings)], clippy::unwrap_used, expect_used, todo, unimplemented, dbg_macro, print_*, unreachable, plus #![warn(clippy::pedantic)].
  • Reproducible buildsrust-toolchain.toml pins 1.85.0; Cargo.lock is committed.
  • Supply chaincargo audit (RustSec) and cargo deny (license + yanked + wildcard policy) run on every push.
  • Cross-platform CI — Linux, macOS, and Windows on stable and MSRV; bare-metal no_std build on thumbv7em-none-eabihf.

Compatibility

  • MSRV: Rust 1.85
  • Edition: 2024
  • Platforms: Linux, macOS, Windows; no_std targets via default-features = false.
  • Public API: unchanged from 0.5.0. Code written against any 0.x version since 0.3 compiles unchanged against 1.0.

Upgrading From 0.x

Bump the version. There is no code migration.

[dependencies]
clock-lib = "1.0"

Out of Scope

clock-lib is deliberately small. It does not handle:

  • Calendar math, date formatting, or timezones — use chrono or time.
  • Timers, sleeping, or scheduling — that's a separate concern that should depend on clock-lib, not be part of it.
  • Async — the readings are synchronous; async code calls them like any other function.

Stability Promise

Anything documented in docs/API.md is part of the 1.x contract. Behavioral changes that would break correct downstream code will only ship with a major version bump. The crate is feature-complete for its scope; future work is bug fixes, performance refinement, and platform support, not new APIs.

Full Changelog: v0.5.0...v1.0.0

v0.5.0 — Bare-Metal Ready

21 May 09:27

Choose a tag to compare

Pre-release

v0.5.0 — Bare-Metal Ready

clock-lib already claimed no_std support; this release proves it. The crate now compiles cleanly against a bare-metal embedded target on every CI run, with the feature split documented in Cargo.toml, the crate-level docs, the README, and the API reference. The library is ready for a 1.0 cut.

Highlights

  • no_std verified, not assumed. A new CI job builds the crate against thumbv7em-none-eabihf (Cortex-M4 with FPU) and against the host target with --no-default-features. Both must pass before a merge.
  • Feature surface, written down. With the default std feature on, you get every reading type and the Clock trait. With std off, you get the VERSION constant — the readings themselves require an OS clock and are honestly gated.
  • Doc polish. Every documentation page now cross-links to every other (Home / API / Performance / Guidelines). The lib.rs crate docs explain the feature flags inline. The performance doc references the REPS regression gate.

What's New

Added

  • CI: no_std build job using thumbv7em-none-eabihf plus a host --no-default-features check.
  • src/lib.rs: feature-flag section in the crate-level documentation.
  • README.md: a no_std installation snippet alongside the standard one.
  • docs/API.md: a Feature Flags subsection under Installation.

Changed

  • docs/PERFORMANCE.md: baseline commands use a generic main name; the REPS-mandated 5% regression gate is called out explicitly.
  • README "Lean & Correct" bullets call out #![forbid(unsafe_code)] and confirm the bare-metal no_std build path is verified, not aspirational.
  • Cross-doc navigation: docs/API.md and docs/GUIDELINES.md now link to docs/PERFORMANCE.md for symmetric navigation across the four core docs.

Compatibility

  • MSRV: Rust 1.85 (unchanged)
  • Edition: 2024 (unchanged)
  • Public API: unchanged from 0.4.x — no code changes required.

Upgrading From 0.4.x

[dependencies]
clock-lib = "0.5"

For no_std consumers:

[dependencies]
clock-lib = { version = "0.5", default-features = false }

What's Next

  • 1.0.0 — stable API. A soak period to confirm no regressions, then publish. The public surface is feature-complete; 1.0 is a stability promise, not a feature drop.

Full Changelog: v0.4.0...v0.5.0

v0.4.0 — Performance, Verified

21 May 08:55

Choose a tag to compare

Pre-release

v0.4.0 — Performance, Verified

The "zero-overhead" claim that has been quietly sitting in the README since day one now ships with the numbers to back it up. clock-lib 0.4.0 adds a real benchmark suite and a docs/PERFORMANCE.md report that proves the wrapper costs the same as std::time — not "approximately the same", measurably the same.

Highlights

  • Real benchmark suite. Seven criterion groups in benches/clock_bench.rs covering monotonic readings, wall readings, unix conversions, the unix helpers end-to-end, elapsed-time measurement, every ManualClock operation, and trait-object dispatch through &dyn Clock.
  • Zero overhead, verified. docs/PERFORMANCE.md records the recorded median for every bench alongside the raw std::time call it forwards to. The wrapper functions sit on top of the bare std functions inside criterion's noise band.
  • Committed baseline. Run cargo bench --bench clock_bench -- --save-baseline 0.4.0 to pin the current numbers, then cargo bench --bench clock_bench -- --baseline 0.4.0 after any change to detect regressions.
  • Hot-path methodology written down. What was measured, how, on what hardware, and how to reproduce it locally — all in the performance doc.

What's New

Added

  • benches/clock_bench.rs — real benchmarks across seven groups:
    • monotonic_nowInstant::now vs clock_lib::now, Monotonic::now, SystemClock::now.
    • wall_nowSystemTime::now vs clock_lib::wall, Wall::now.
    • unix_conversions — cost of Wall::unix_seconds / unix_millis / unix_nanos on a pre-captured reading.
    • unix_helpers — end-to-end cost of clock_lib::unix / unix_ms / unix_ns.
    • elapsedInstant::elapsed vs Monotonic::elapsed vs clock_lib::elapsed.
    • manual_clocknow, wall, offset, advance(1ns).
    • dyn_dispatch — concrete SystemClock vs &dyn Clock.
  • docs/PERFORMANCE.md — methodology, recorded numbers, the zero-overhead claim, and how to compare against the baseline.
  • README link to the performance document.

Changed

  • Replaced the placeholder bench function with the real suite.

Compatibility

  • MSRV: Rust 1.85 (unchanged)
  • Edition: 2024 (unchanged)
  • Public API: unchanged — no migration required from 0.3.x.

Upgrading From 0.3.x

[dependencies]
clock-lib = "0.4"

No code changes required.

What's Next

  • 0.5.0 — no_std hardening and the 1.0 release candidate.
  • 1.0.0 — stable.

Full Changelog: v0.3.0...v0.4.0

v0.3.0 — The Mockable Clock

21 May 08:39

Choose a tag to compare

Pre-release

v0.3.0 — The Mockable Clock

The headline feature of clock-lib lands in this release: a Clock trait with two implementations — SystemClock for production and ManualClock for tests — so timing-driven code becomes deterministically testable without ever calling thread::sleep.

Highlights

  • Clock trait. A Send + Sync source of time exposing now() -> Monotonic and wall() -> Wall. Take it as a dependency anywhere your code reads the clock; substitute it in tests.
  • SystemClock. Zero-sized, Copy, const-constructible. Forwards directly to the OS readings already in the crate — nothing extra to pay over the free functions.
  • ManualClock. Lock-free advance via an AtomicU64 offset. The clock never moves on its own. Tests advance it forward in arbitrary increments and observe the timing-driven behavior exactly.
  • Blanket impls. Clock is implemented for Arc<C> and &C where C: Clock, so shared ownership and borrowed access work without re-implementing the trait.
  • TTL test pattern, no sleep. The classic "is this expired?" check now takes 8 lines and zero milliseconds of real wall-clock time:
use clock_lib::{Clock, ManualClock, Monotonic};
use std::time::Duration;

fn expired<C: Clock>(clock: &C, stamp: Monotonic, ttl: Duration) -> bool {
    clock.now().duration_since(stamp) >= ttl
}

let clock = ManualClock::new();
let stamp = clock.now();
assert!(!expired(&clock, stamp, Duration::from_secs(60)));
clock.advance(Duration::from_secs(60));
assert!(expired(&clock, stamp, Duration::from_secs(60)));

What's New

Added

  • Clock trait with now and wall methods (Send + Sync).
  • SystemClock struct (Copy, const-constructible).
  • ManualClock struct with new, advance, and offset methods backed by an atomic offset.
  • Blanket Clock impls for Arc<C: Clock> and &C: Clock.
  • Integration tests covering: zero-advance idempotence, exact-advance semantics on both monotonic and wall, offset accumulation, the sleep-free TTL pattern, Arc<ManualClock> dispatch, and Arc<dyn Clock> trait-object usage.
  • Doctests on every public item in the new module.

Changed

  • Monotonic and Wall fields are now pub(crate) (internal change — field access remains forbidden outside the crate, but ManualClock can construct synthetic readings).

Compatibility

  • MSRV: Rust 1.85 (unchanged)
  • Edition: 2024 (unchanged)
  • Public API: strictly additive. All 0.2.x code continues to compile unchanged.

Upgrading From 0.2.x

[dependencies]
clock-lib = "0.3"

No code changes required. To adopt the mockable clock pattern, take a C: Clock (or &dyn Clock, or Arc<dyn Clock>) as a dependency wherever you call clock_lib::now() today, and pass SystemClock in production. Your tests can then swap to ManualClock and drop every thread::sleep.

What's Next

  • 0.4.0 — performance verification. Criterion benchmarks for reading latency on both SystemClock and ManualClock, with committed baselines proving zero overhead over raw std::time.
  • 0.5.0 — no_std hardening and the 1.0 release candidate.

Full Changelog: v0.2.1...v0.3.0

v0.2.1 — Hardening Pass

21 May 08:25

Choose a tag to compare

Pre-release

v0.2.1 — Hardening Pass

A polish release behind the 0.2.0 API. No public surface changes — every existing program keeps working — but the supply chain, build determinism, and lint posture all tighten up to match the project's full engineering bar.

Highlights

  • #![forbid(unsafe_code)] at the crate root. The "no unsafe in clock-lib" promise is now enforced by the compiler at the strongest possible level: downstream code cannot opt the crate out of this guarantee.
  • Stricter lint stack. #![deny(warnings)] and #![deny(clippy::unreachable)] join the existing clippy::unwrap_used, expect_used, todo, unimplemented, dbg_macro, and print_* denies. A warning is a build failure.
  • Reproducible builds. The Rust toolchain is pinned in rust-toolchain.toml, and Cargo.lock is committed for deterministic dependency resolution across environments.
  • Supply-chain checks in CI. cargo audit scans the dependency tree against the RustSec advisory database on every push, and cargo deny enforces a license allowlist, yanked-crate ban, and wildcard ban.
  • Cross-platform line-ending normalization. A new .gitattributes pins source files to LF so Windows checkouts no longer break cargo fmt --check.
  • CI runtime refreshed. actions/checkout upgraded to v5. The Security audit job uses pre-built binaries via taiki-e/install-action, avoiding both Node.js 20 deprecation warnings and tooling MSRV friction.
  • Test naming aligned with REPS. Every integration test now follows the test_<subject>_<condition>_<expected> convention.

Compatibility

  • MSRV: Rust 1.85 (unchanged)
  • Edition: 2024 (unchanged)
  • Public API: unchanged — no migration required from 0.2.0.

Upgrading From 0.2.0

[dependencies]
clock-lib = "0.2"

That's it. The cargo resolver picks up 0.2.1 automatically and nothing in your code needs to change.

What's Next

  • 0.3.0 — the Clock trait. SystemClock for production, ManualClock for deterministic, sleep-free tests. The mockable-clock pattern that makes clock-lib valuable in test suites.
  • 0.4.0 — performance verification. Criterion benchmarks with committed baselines proving the abstraction is zero-cost over raw std::time.
  • 0.5.0 — no_std hardening and the 1.0 release candidate.

Full Changelog: https://github.com/jamesgober/clock-lib/commits/v0.2.1