A Rust crate for portable timers that handle system sleep consistently.
Time in the standard library, tokio, and generally every Rust crate has platform-dependent behavior:
- Linux/Android:
CLOCK_MONOTONICexcludes time spent sleeping (rust-lang/rust#71860) - macOS/iOS:
CLOCK_UPTIME_RAW(ormach_absolute_time) excludes time spent sleeping - Windows:
QueryPerformanceCounterincludes time spent sleeping (rust-lang/rust#79462)
This makes it difficult to write portable code with predictable timer behavior, especially in applications that need to account for the real-world time that has passed (including system sleep).
systime offers a simple API where you explicitly choose whether to track or ignore system sleep.
Sleep is a blanket term for any time a process spends suspended. Real-world time passes, but a process that isn't executing and won't see it. A clock that tracks sleep is still monotonic, it just has sudden jumps.
A few examples:
- Doze on Android
- Suspend to RAM on desktop platforms
- Suspended apps on iOS
- ✅ Linux/Android:
timerfdwithCLOCK_MONOTONIC(ignore sleep) orCLOCK_BOOTTIME(track sleep) - ✅ macOS/iOS:
kqueue+EVFILT_TIMERwith mach absolute time (ignore sleep) or mach continuous time (track sleep) - ~ Windows: I/O completion ports + high-resolution waitable timers (incomplete, sleep is always tracked)
systime supports both the tokio and smol async ecosystems via feature flags.
Tokio support is enabled by default.
Tracking sleep is useful for:
- Networking timers: Any timeouts, keepalives, and other networking timers likely need to be aware of system sleep.
- Authentication: Credentials may expire sooner than expected unless system sleep is tracked.
- User-facing scheduled tasks: Run tasks at specific intervals regardless of system sleep (i.e. UI reminder popup).
- Consistent cross-device timing: When correlating events between devices, it's important to know the real world time that has passed.
Ignoring sleep is useful for:
- Internal scheduled tasks: Run tasks at specific intervals in accordance with time spent executing (i.e. garbage collection).
- Performance monitoring / profiling: Client-side metrics probably shouldn't track system sleep since it adds wild, inaccurate p99/max values to your dashboards.
While every effort has been made to ensure timers are fast and efficient (i.e. sub-millisecond precision on Windows), this crate does use system timer facilities and there are limits on these resources. Hence why most operations can return an error.
Use these timers sparingly and include a fallback to the appropriate equivalent (i.e. tokio::time for tokio) in case of failure. Alternatively, a timer wheel-esque approach could be used.