<div align="center">
<img src="./logo.png" alt="Tick Logo" width="128">
# Tick
[](https://crates.io/crates/tick)
[](https://docs.rs/tick)
[](https://crates.io/crates/tick)
[](https://github.com/microsoft/oxidizer/actions)
[](https://codecov.io/gh/microsoft/oxidizer)
[](../LICENSE)
</div>
* [Summary](#summary)
* [Why?](#why)
* [Overview](#overview)
* [Machine-Centric vs. Human-Centric Time](#machine-centric-vs-human-centric-time)
* [Testing](#testing)
* [Examples](#examples)
* [Additional Examples](#additional-examples)
## Summary
Primitives for obtaining, working with, and mocking system
time and timers, enabling faster and more robust testing.
## Quick Start
```rust
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.
[jiff]: https://crates.io/crates/jiff
[chrono]: https://crates.io/crates/chrono
[time]: https://crates.io/crates/time
## 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.
```rust
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`].
```rust
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
```rust
use std::time::Duration;
use tick::Clock;
let stopwatch = clock.stopwatch();
// Perform some operation...
stopwatch.elapsed()
```
### Use `Clock` to create a `PeriodicTimer`
```rust
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](https://tokio.rs/) 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](https://serde.rs/).
- **`fmt`** - Enables the [`fmt`] module with utilities for formatting `SystemTime` into
various formats (e.g., ISO 8601, RFC 2822).
## Additional Examples
The [time examples](https://github.com/microsoft/oxidizer/tree/main/crates/tick/examples)
contain additional examples of how to use the time primitives.
<div style="font-size: 75%" ><hr/>
This crate was developed as part of [The Oxidizer Project](https://github.com/microsoft/oxidizer).
</div>