3 unstable releases

Uses new Rust 2024

new 0.1.0 Dec 16, 2025
0.0.1 Dec 5, 2015
0.0.0 Sep 9, 2015

#172 in Data structures

MIT license

155KB
2K SLoC

Tick Logo

Tick

crate.io docs.rs MSRV CI Coverage License

Summary

Primitives for obtaining, working with, and mocking system time and timers, enabling faster and more robust testing.

Quick Start

use std::time::Duration;
use tick::{Clock, Delay};

async fn produce_value(clock: &Clock) -> u64 {
    let stopwatch = clock.stopwatch();
    clock.delay(Duration::from_secs(60)).await;
    println!("elapsed time: {}ms", stopwatch.elapsed().as_millis());
    123
}

#[tokio::main]
async fn main() {
    let clock = Clock::new_tokio();
    let value = produce_value(&clock).await;
    assert_eq!(value, 123);
}

#[cfg(test)]
mod tests {
    use super::*;
    use tick::ClockControl;

    #[tokio::test]
    async fn test_produce_value() {
        // Automatically advance timers for instant, deterministic testing
        let clock: Clock = ClockControl::new().auto_advance_timers(true).to_clock();
        assert_eq!(produce_value(&clock).await, 123);
    }
}

Why?

This crate provides a unified API for working with time that:

  • Easy async runtime integration - Provides built-in support for Tokio and can be extended to work with other runtimes without tight coupling to any specific implementation.
  • Enables deterministic testing - With the test-util feature, ClockControl lets you manipulate the passage of time—advance it instantly, pause it, or jump forward. No waiting for a 1-minute periodic job in your tests.
  • Improves testability - Time-dependent code becomes fast and reproducible to test without relying on wall-clock time.

The testability features are transparent to consumers—code using Clock works identically in production and tests, with zero runtime overhead when test-util is disabled.

Overview

  • Clock - Provides an abstraction for time-related operations. Returns absolute time as SystemTime and relative time measurements via stopwatch. Used when creating other time primitives.
  • ClockControl - Controls the passage of time. Available when the test-util feature is enabled.
  • Stopwatch - Measures elapsed time.
  • Delay - Delays the execution for a specified duration.
  • PeriodicTimer - Schedules a task to run periodically.
  • FutureExt - Extensions for the Future trait.
  • Error - Represents an error that can occur when working with time. Provides limited introspection capabilities.
  • [fmt] - Utilities for formatting SystemTime into various formats. Available when the fmt feature is enabled.
  • runtime - Infrastructure for integrating time primitives into async runtimes.

Machine-Centric vs. Human-Centric Time

When working with time, two different use cases are considered:

  • Machine-Centric - Measuring time intervals such as timeouts, periodic activities, cache TTLs, etc. For persistent data, this includes storing, retrieving, and manipulating timestamps, as well as parsing timestamps in well-known formats such as ISO 8601. Machine-centric time has little ambiguity.
  • Human-Centric - Wall clock time, formatting, parsing, time zones, calendars. Dealing with human-centric time involves significant ambiguity.

This crate is designed for machine-centric time. For human-centric time manipulation, consider using other crates such as jiff, chrono, or time. The time primitives in this crate are designed for easy interoperability with these crates. See the time_interop* examples for more details.

Testing

This crate provides a way to control the passage of time in tests via the ClockControl type, which is exposed when the test-util feature is enabled.

Important: Never enable the test-util feature for production code. Only use it in your dev-dependencies.

Examples

Use Clock to retrieve absolute time

The clock provides absolute time as SystemTime. See Clock documentation for detailed information.

use std::time::{Duration, SystemTime};

use tick::Clock;

// Using SystemTime for basic absolute time needs
let time1: SystemTime = clock.system_time();
let time2: SystemTime = clock.system_time();

// Time is always moving forward. Note that system time might be
// adjusted by the operating system between calls.
assert!(time1 <= time2);

Use Clock to retrieve relative time

The clock provides relative time via Clock::instant and Stopwatch.

use std::time::{Duration, Instant};

use tick::Clock;

// Using clock.stopwatch() for convenient elapsed time measurement
let stopwatch = clock.stopwatch();
// Perform some operation...
let elapsed: Duration = stopwatch.elapsed();

// Using Clock::instant for lower-level access to monotonic time
let start: Instant = clock.instant();
// Perform some operation...
let end: Instant = clock.instant();

Use Stopwatch for measurements

use std::time::Duration;

use tick::Clock;

let stopwatch = clock.stopwatch();
// Perform some operation...
stopwatch.elapsed()

Use Clock to create a PeriodicTimer

use std::time::Duration;

use futures::StreamExt;
use tick::{Clock, PeriodicTimer};

// Delay for 10ms before the timer starts ticking
clock.delay(Duration::from_millis(10)).await;

let timer = PeriodicTimer::new(clock, Duration::from_millis(1));

timer
    .take(3)
    .for_each(async |()| {
        // Do something every 1ms
    })
    .await;

Features

This crate provides several optional features that can be enabled in your Cargo.toml:

  • tokio - Integration with the Tokio runtime. Enables Clock::new_tokio for creating clocks that use Tokio's time facilities.
  • test-util - Enables the ClockControl type for controlling the passage of time in tests. This allows you to pause time, advance it manually, or automatically advance timers for fast, deterministic testing. Only enable this in dev-dependencies.
  • serde - Adds serialization and deserialization support via serde.
  • fmt - Enables the [fmt] module with utilities for formatting SystemTime into various formats (e.g., ISO 8601, RFC 2822).

Additional Examples

The time examples contain additional examples of how to use the time primitives.


This crate was developed as part of The Oxidizer Project.

Dependencies

~0–2MB
~30K SLoC