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 clock for testing
- MockNanoClock: Nanosecond-precision controllable clock for testing
- MockClockProgression: Switch mock clocks between frozen and monotonic progression
- 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
- Mock clock with controllable time
- Mock sleeper with controllable monotonic elapsed time
- Set time to specific points
- Advance time programmatically
- Auto-increment support
Installation
Add this to your Cargo.toml:
[]
= "0.7"
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!;
Mockable Relative Sleeps
use ;
use Duration;
let sleeper = new;
let worker = sleeper.clone;
spawn;
sleeper.advance;
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 clock for testing
- Thread-safe with
Arc<Mutex<>> - Supports time setting, advancement, and auto-increment
- Frozen by default for deterministic tests
- Can switch to monotonic progression when natural elapsed time is needed
- Use for: unit tests, integration tests, time-dependent logic testing
MockNanoClock
- Nanosecond-precision controllable clock for testing
- Implements
Clock,NanoClock, andControllableClock - Frozen by default for deterministic tests
- Can switch to monotonic progression when natural elapsed time is needed
- Supports nanosecond advancement and auto-increment
- Use for: deterministic tests around
NanoClockandNanoTimeMeter
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
- Manually controlled relative sleeper for deterministic tests that implements
Sleeper - Starts at elapsed time zero
- Clones share the same elapsed time
- Supports manual
set_elapsed,advance, andreset - 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
MockClockorMockNanoClockwhen tests need controllable logical time. They freeze by default, support instant manual advancement, can auto-advance on each read, and can opt into monotonic progression when a test needs natural elapsed time. - 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 acceptMockNanoClockor any otherNanoClockimplementation. - Use
SystemSleeperorMockSleeperwhen code needs injectable relative sleeps. They implement the blockingSleepertrait, and also implementAsyncSleeperwhen thetokiofeature is enabled. Mock sleepers let tests advance elapsed time instantly instead of waiting for wall-clock time. - 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 while preserving progression and auto-advance settingsadd_duration(duration)- Advances the clock by a durationreset()- Resets the clock to initial state
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 manually advanced elapsed time for 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
- 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 ;
use Duration;
let sleeper = new;
let worker = sleeper.clone;
let handle = spawn;
sleeper.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.