Releases: jamesgober/clock-lib
v1.0.0 — Stable API
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
Monotonic—now,elapsed,duration_since,checked_duration_since,saturating_duration_since.Wall—now,unix_seconds,unix_millis,unix_nanos. Pre-epoch system clocks saturate to0; no panics, no silent wrapping.
Trait + implementations
Clock—Send + Synctrait withnowandwallmethods. Blanket impls forArc<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 thestdfeature.
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 builds —
rust-toolchain.tomlpins 1.85.0;Cargo.lockis committed. - Supply chain —
cargo audit(RustSec) andcargo deny(license + yanked + wildcard policy) run on every push. - Cross-platform CI — Linux, macOS, and Windows on stable and MSRV; bare-metal
no_stdbuild onthumbv7em-none-eabihf.
Compatibility
- MSRV: Rust 1.85
- Edition: 2024
- Platforms: Linux, macOS, Windows;
no_stdtargets viadefault-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
chronoortime. - 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
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_stdverified, not assumed. A new CI job builds the crate againstthumbv7em-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
stdfeature on, you get every reading type and theClocktrait. Withstdoff, you get theVERSIONconstant — 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_stdbuild job usingthumbv7em-none-eabihfplus a host--no-default-featurescheck. src/lib.rs: feature-flag section in the crate-level documentation.README.md: ano_stdinstallation snippet alongside the standard one.docs/API.md: a Feature Flags subsection under Installation.
Changed
docs/PERFORMANCE.md: baseline commands use a genericmainname; the REPS-mandated 5% regression gate is called out explicitly.- README "Lean & Correct" bullets call out
#![forbid(unsafe_code)]and confirm the bare-metalno_stdbuild path is verified, not aspirational. - Cross-doc navigation:
docs/API.mdanddocs/GUIDELINES.mdnow link todocs/PERFORMANCE.mdfor 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
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
criteriongroups inbenches/clock_bench.rscovering monotonic readings, wall readings, unix conversions, the unix helpers end-to-end, elapsed-time measurement, everyManualClockoperation, and trait-object dispatch through&dyn Clock. - Zero overhead, verified.
docs/PERFORMANCE.mdrecords the recorded median for every bench alongside the rawstd::timecall 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.0to pin the current numbers, thencargo bench --bench clock_bench -- --baseline 0.4.0after 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_now—Instant::nowvsclock_lib::now,Monotonic::now,SystemClock::now.wall_now—SystemTime::nowvsclock_lib::wall,Wall::now.unix_conversions— cost ofWall::unix_seconds/unix_millis/unix_nanoson a pre-captured reading.unix_helpers— end-to-end cost ofclock_lib::unix/unix_ms/unix_ns.elapsed—Instant::elapsedvsMonotonic::elapsedvsclock_lib::elapsed.manual_clock—now,wall,offset,advance(1ns).dyn_dispatch— concreteSystemClockvs&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_stdhardening and the 1.0 release candidate. - 1.0.0 — stable.
Full Changelog: v0.3.0...v0.4.0
v0.3.0 — The Mockable Clock
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
Clocktrait. ASend + Syncsource of time exposingnow() -> Monotonicandwall() -> 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 anAtomicU64offset. The clock never moves on its own. Tests advance it forward in arbitrary increments and observe the timing-driven behavior exactly.- Blanket impls.
Clockis implemented forArc<C>and&CwhereC: 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
Clocktrait withnowandwallmethods (Send + Sync).SystemClockstruct (Copy,const-constructible).ManualClockstruct withnew,advance, andoffsetmethods backed by an atomic offset.- Blanket
Clockimpls forArc<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, andArc<dyn Clock>trait-object usage. - Doctests on every public item in the new module.
Changed
MonotonicandWallfields are nowpub(crate)(internal change — field access remains forbidden outside the crate, butManualClockcan 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
SystemClockandManualClock, with committed baselines proving zero overhead over rawstd::time. - 0.5.0 —
no_stdhardening and the 1.0 release candidate.
Full Changelog: v0.2.1...v0.3.0
v0.2.1 — Hardening Pass
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 "nounsafeinclock-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 existingclippy::unwrap_used,expect_used,todo,unimplemented,dbg_macro, andprint_*denies. A warning is a build failure. - Reproducible builds. The Rust toolchain is pinned in
rust-toolchain.toml, andCargo.lockis committed for deterministic dependency resolution across environments. - Supply-chain checks in CI.
cargo auditscans the dependency tree against the RustSec advisory database on every push, andcargo denyenforces a license allowlist, yanked-crate ban, and wildcard ban. - Cross-platform line-ending normalization. A new
.gitattributespins source files to LF so Windows checkouts no longer breakcargo fmt --check. - CI runtime refreshed.
actions/checkoutupgraded tov5. The Security audit job uses pre-built binaries viataiki-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
Clocktrait.SystemClockfor production,ManualClockfor deterministic, sleep-free tests. The mockable-clock pattern that makesclock-libvaluable 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_stdhardening and the 1.0 release candidate.
Full Changelog: https://github.com/jamesgober/clock-lib/commits/v0.2.1