Qubit Clock
Thread-safe clock and sleep abstractions for Rust with monotonic and mock implementations.
Overview
Qubit Clock provides a flexible and type-safe clock abstraction system for Rust applications. It offers robust, thread-safe clock implementations with support for basic time access, high-precision measurements, timezone handling, monotonic time, mockable relative sleeps, and testing support.
Features
🕐 Clock Abstractions
- Trait-based Design: Flexible clock abstraction through orthogonal traits
- Interface Segregation: Don't force implementations to provide features they don't need
- Composition over Inheritance: Extend functionality through wrappers
- Zero-Cost Abstractions: Pay only for what you use
⏰ Clock Implementations
- SystemClock: Uses system wall clock time
- MonotonicClock: Monotonic time (unaffected by system time changes)
- NanoMonotonicClock: Monotonic time with nanosecond precision
- MockClock: Controllable UTC and nanosecond clock backed by a mock timeline
- MockTimeline: Shared monotonic mock time source for deterministic tests
- MockTime: Convenience facade bundling one timeline, clock, and sleeper
- Zoned<C>: Wrapper that adds timezone support to any clock
⏱️ Time Meters
- TimeMeter: Millisecond-precision time measurement for general use
- NanoTimeMeter: Nanosecond-precision time measurement for high-precision needs
- Human-Readable Output: Format elapsed time in readable strings
- Speed Calculation: Calculate processing speed (items per second/minute)
- Test-Friendly: Support injecting mock clocks for deterministic testing
⏲️ Sleep Abstractions
- Sleeper: Blocking relative sleep abstraction
- AsyncSleeper: Tokio async relative sleep abstraction
- SystemSleeper: Real elapsed-time sleeper that implements
Sleeper, and also implementsAsyncSleeperwhen thetokiofeature is enabled - MockSleeper: Deterministic elapsed-time sleeper that implements
Sleeper, and also implementsAsyncSleeperwhen thetokiofeature is enabled
🔒 Thread Safety
- All clock implementations are
Send + Sync - Immutable design for system and monotonic clocks
- Fine-grained locking for mock clock
- Safe to share across threads
🌍 Timezone Support
- Convert UTC time to any timezone
- Wrap any clock with timezone support
- Based on
chrono-tzfor comprehensive timezone database
🧪 Testing Support
- Shared mock timeline for clocks, sleepers, and timeout-aware test utilities
- Mock clock with controllable wall-clock and nanosecond readings
- Mock sleeper with controllable monotonic elapsed time
- Set time to specific points
- Advance time programmatically
Installation
Add this to your Cargo.toml:
[]
= "0.8"
Quick Start
Basic Usage
use ;
let clock = new;
let timestamp = clock.millis;
let time = clock.time;
println!;
With Timezone
use ;
use Shanghai;
let clock = new;
let local = clock.local_time;
println!;
Monotonic Time for Performance Measurement
use ;
use thread;
use Duration;
let clock = new;
let start = clock.millis;
sleep;
let elapsed = clock.millis - start;
println!;
Testing with MockClock
use ;
use ;
let clock = new;
// Set to a specific time
let fixed_time = parse_from_rfc3339.unwrap.with_timezone;
clock.set_time;
assert_eq!;
// Advance time
clock.add_duration;
assert_eq!;
High-Precision Measurements
use ;
let clock = new;
let start = clock.nanos;
// Perform some operation
for _ in 0..1000
let elapsed = clock.nanos - start;
println!;
Time Meters for Elapsed Time Measurement
use TimeMeter;
use thread;
use Duration;
let mut meter = new;
meter.start;
sleep;
meter.stop;
println!;
Unified Mock Time for Tests
use ;
use Sleeper;
use Duration;
let mock = unix_epoch;
let clock = mock.clock;
let sleeper = mock.sleeper;
let worker = sleeper.clone;
let handle = spawn;
assert!;
mock.advance;
handle.join.expect;
assert_eq!;
High-Precision Time Meter
use NanoTimeMeter;
let mut meter = new;
meter.start;
// Perform some operation
for _ in 0..1000
meter.stop;
println!;
println!;
Speed Calculation with Time Meter
use TimeMeter;
use thread;
use Duration;
let mut meter = new;
meter.start;
// Process 1000 items
for _ in 0..1000
meter.stop;
println!;
println!;
Architecture
The crate is built around several orthogonal traits:
- Clock: Base trait providing UTC time
- NanoClock: Extension for nanosecond precision
- ZonedClock: Extension for timezone support
- ControllableClock: Extension for time control (testing)
- Sleeper: Blocking relative sleep operations
- AsyncSleeper: Tokio async relative sleep operations
This design follows the Interface Segregation Principle, ensuring that implementations only need to provide the features they actually support.
Clock Implementations
SystemClock
- Based on system wall clock time
- Subject to system time adjustments (NTP, manual changes)
- Zero-sized type (ZST) with no runtime overhead
- Use for: logging, timestamps, general time queries
MonotonicClock
- Based on
std::time::Instant(monotonically increasing) - Unaffected by system time adjustments
- Millisecond precision
- Records base point on creation
- Use for: performance monitoring, timeout control, time interval measurements
NanoMonotonicClock
- Based on
std::time::Instantwith nanosecond precision - Unaffected by system time adjustments
- Higher precision than
MonotonicClock - Use for: high-precision measurements, microbenchmarking
MockClock
- Controllable UTC and nanosecond clock for testing
- Backed by a
MockTimeline - Thread-safe with shared state guarded by non-poisoning synchronization
- Implements
Clock,NanoClock, andControllableClock - Supports setting the wall-clock anchor and advancing shared mock time
- Frozen until the associated timeline is advanced by the test
- Use for: unit tests, integration tests, time-dependent logic testing
MockTimeline
- Shared monotonic mock time source
- Drives
MockClock,MockSleeper, and future timeout-aware test primitives - Supports instant time advancement and external event notifications
- Tracks active waiters so reset can reject unsafe timeline rewinds
- Assigns a unique id to each timeline so
MockInstantdeadlines cannot be used with the wrong timeline - Rejects deadlines created by a different mock timeline
- Use for: deterministic tests that need clocks and sleeps to follow one time source
MockTime
- Convenience facade around one
MockTimeline,MockClock, andMockSleeper advance(duration)moves all associated components togetherset_time(instant)reanchors the clock at the current timeline instant- Use for: tests that need both "current time" and "elapsed sleep" control
Zoned<C>
- Wrapper that adds timezone support to any clock
- Generic over any
Clockimplementation - Converts UTC time to local time in specified timezone
- Use for: displaying local time, timezone conversions
Sleep Implementations
SystemSleeper
- Implements
Sleeperusingstd::thread::sleepfor blocking relative sleeps - Implements
AsyncSleeperusingtokio::time::sleepwhen thetokiofeature is enabled - Zero-sized type (ZST) with no runtime state
- Use for: production code that needs an injectable relative sleeper
MockSleeper
- Timeline-backed relative sleeper for deterministic tests that implements
Sleeper - Uses the elapsed time from its backing
MockTimeline - Clones share the same timeline
- Complete sleeps by advancing the timeline through
MockTimelineorMockTime - Implements
AsyncSleeperfor asynchronous sleeps when thetokiofeature is enabled - Use for: testing retry and backoff logic without waiting for real time
Time Meters
TimeMeter
A millisecond-precision time meter for measuring elapsed time with the following features:
- Flexible Clock Source: Supports any clock implementing
Clocktrait - Default to MonotonicClock: Uses monotonic time by default for stable measurements
- Multiple Output Formats: Milliseconds, seconds, minutes, and human-readable format
- Speed Calculation: Calculate processing speed (items per second/minute)
- Real-Time Monitoring: Get elapsed time without stopping the meter
- Test-Friendly: Inject
MockClockfor deterministic testing
Example output formats:
123 ms- Less than 1 second1.5s- 1-60 seconds1m 23s- More than 1 minute1h 1m 5s- More than 1 hour
NanoTimeMeter
A nanosecond-precision time meter with features similar to TimeMeter:
- Nanosecond Precision: Based on
NanoClocktrait - Default to NanoMonotonicClock: Uses high-precision monotonic time
- Human-Readable Output: Automatically chooses appropriate unit (ns, μs, ms, s, m, h)
- Speed Calculation: High-precision speed calculation
- Test-Friendly: Supports any injected test clock that implements
NanoClock
Example output formats:
123 ns- Less than 1 microsecond123.4 μs- 1-1000 microseconds123.4 ms- 1-1000 milliseconds1.5s- 1-60 seconds1m 23s- More than 1 minute1h 1m 5s- More than 1 hour
Why Not Just Use std::time::Instant?
std::time::Instant is the right primitive for measuring real elapsed time in
production code. It is monotonic, fast, and simple:
let start = now;
// do work
let elapsed = start.elapsed;
This crate exists for the cases where elapsed-time measurement is only part of the problem:
- Use
MockClockwhen tests need controllable logical time. It implements bothClockandNanoClock, freezes until the mock timeline advances, and can be reanchored to any UTC instant. - Use
TimeMeterorNanoTimeMeterwhen application code needs a reusable start/stop meter with formatted durations, speed calculations, and an injectable clock source. They use monotonic clocks by default;TimeMetercan acceptMockClock, whileNanoTimeMetercan acceptMockClockor any otherNanoClockimplementation. - Use
SystemSleeperorMockSleeperwhen code needs injectable relative sleeps. They implement the blockingSleepertrait, and also implementAsyncSleeperwhen thetokiofeature is enabled. Mock sleepers complete when their backingMockTimelineadvances instead of waiting for wall-clock time. - Use
MockTimewhen one test needs a clock and sleeper to share the same mock elapsed time source. - Use the
Clocktraits when business logic depends on "current time" and must be testable without coupling directly to the system clock orInstant::now().
In short, Instant measures real elapsed time; mock clocks make time
controllable for tests; time meters turn elapsed-time measurement into a
reusable, formatted, testable abstraction.
API Reference
Clock Trait
The core Clock trait provides:
millis()- Returns current time in milliseconds since Unix epochtime()- Returns current time asDateTime<Utc>
NanoClock Trait
Extension trait for high-precision clocks:
nanos()- Returns current time in nanoseconds since Unix epochtime_precise()- Returns high-precisionDateTime<Utc>
ZonedClock Trait
Extension trait for timezone support:
timezone()- Returns the clock's timezonelocal_time()- Returns current time in the clock's timezone
Use Zoned::new(clock, tz) to select the timezone for a clock.
ControllableClock Trait
Extension trait for controllable clocks (testing):
set_time(instant)- Reanchors controllable mock clocks to a logical time at the current timeline instantadd_duration(duration)- Advances the clock by a non-negative durationreset()- Resets the clock to initial state
Mock Time Runtime
Mock time APIs are available at the crate root:
MockTimeline- Shared monotonic mock time sourceMockInstant- Timeline-bound monotonic instant used for deadlinesMockClock- Timeline-backed implementation ofClock,NanoClock, andControllableClockMockTime- Convenience facade that returns one shared clock and sleeperMockWaiterKind- Waiter category used for test observabilityMockTimeError- Error returned when reset is rejected with active waiters or a deadline belongs to a different mock timeline
Sleep Traits
Sleep APIs are available under qubit_clock::sleep:
Sleeper- Provides blocking relative sleepsAsyncSleeper- Provides Tokio async relative sleepsSystemSleeper- Uses real elapsed time, implementsSleeper, and also implementsAsyncSleeperwhen thetokiofeature is enabledMockSleeper- Uses aMockTimelinefor deterministic tests, implementsSleeper, and also implementsAsyncSleeperwhen thetokiofeature is enabled
Sleep APIs intentionally model only relative sleeping. Notification waits,
condition waits, and timeout waits belong to monitor primitives in qubit-lock.
Design Principles
Interface Segregation
The crate follows the Interface Segregation Principle by providing separate traits for different capabilities:
- Not all clocks need nanosecond precision →
NanoClockis separate - Not all clocks need timezone support →
ZonedClockis separate - Only test clocks need controllability →
ControllableClockis separate - Only relative sleep users need sleep injection → sleep traits live under
sleep
This allows implementations to provide only the features they need, keeping the API clean and focused.
Single Responsibility
Each trait and type has one clear purpose:
Clock- Provide UTC timeNanoClock- Provide high-precision timeZonedClock- Provide timezone conversionControllableClock- Provide time control for testingSleeper- Provide blocking relative sleepsAsyncSleeper- Provide Tokio async relative sleeps
Composition over Inheritance
Functionality is extended through wrappers rather than inheritance:
Zoned<C>wraps anyClockto add timezone support- Time meters accept any
Clockimplementation via generics
Zero-Cost Abstractions
The design ensures you only pay for what you use:
SystemClockandMonotonicClockare zero-sized or minimal overhead- Trait methods are often inlined
- Generic code is monomorphized at compile time
Testing & Code Coverage
This project maintains comprehensive test coverage with detailed validation of all functionality.
Running Tests
# Run all tests
# Run with coverage report
# Generate text format report
# Run CI checks (tests, lints, formatting)
Dependencies
- chrono: Date and time handling with serialization support
- chrono-tz: Comprehensive timezone database
- parking_lot: Non-poisoning synchronization primitives used by the mock time runtime
- tokio: Optional dependency for
sleep::AsyncSleeperwhen thetokiofeature is enabled
Use Cases
Performance Monitoring
use TimeMeter;
let mut meter = new;
meter.start;
// Perform operation
process_data;
meter.stop;
info!;
Mockable Sleep Control
use MockTime;
use Sleeper;
use Duration;
let mock = unix_epoch;
let sleeper = mock.sleeper;
let worker = sleeper.clone;
let handle = spawn;
assert!;
mock.advance;
handle.join.expect;
Testing Time-Dependent Logic
use ;
use Duration;
Benchmarking
use NanoTimeMeter;
let mut meter = new;
meter.start;
for _ in 0..10000
meter.stop;
println!;
License
Copyright (c) 2025 - 2026. Haixing Hu.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
See LICENSE for the full license text.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Author
Haixing Hu - Qubit Co. Ltd.
For more information about the Qubit open source projects, visit our GitHub homepage.