---
title: How to Add and Subtract Time in Rust
summary: Add and subtract time in Rust by using standard arithmetic operators on numeric duration values.
lang: en
slug: how-to-add-and-subtract-time-in-rust
published: 2026-04-17T12:05:42+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/how-to-add-and-subtract-time-in-rust/
---

# How to Add and Subtract Time in Rust



## When time arithmetic gets tricky

You are building a rate limiter for an API client. You store the timestamp of the last request, add the cooldown duration, and compare the result against the current time. In Python, you might write `last_request + timedelta(seconds=5)`. In JavaScript, you add milliseconds to a `Date` object. In Rust, the compiler rejects your code before it runs. There is no `+` operator for time.

Rust splits time into distinct types to prevent silent bugs. A span of time is not the same as a point in time. A wall-clock timestamp can jump backwards due to system adjustments, while a monotonic timer never goes backward. Mixing these concepts requires explicit methods, not operators. This design forces you to handle overflow, clock jumps, and calendar quirks at the call site. You cannot accidentally subtract a duration from a timestamp and get a nonsense value. The type system blocks the mistake.

## Duration: the span of time

`Duration` represents a span of time. It is a struct containing seconds and nanoseconds. You use `Duration` for timeouts, intervals, and elapsed time. It lives in `std::time`.

`Duration` supports arithmetic with other `Duration` values. You can add and subtract them using the `+` and `-` operators. The `+` operator is safe and never panics. The `-` operator panics if the result would be negative. This panic is intentional. A negative duration has no meaning in Rust's model. If you need to handle cases where the second duration is larger, use `checked_sub`, which returns `Option<Duration>`.

```rust
use std::time::Duration;

fn main() {
    // Duration stores seconds as u64 and nanoseconds as u32.
    // Arithmetic handles the carry between seconds and nanoseconds automatically.
    let five_seconds = Duration::from_secs(5);
    let two_seconds = Duration::from_secs(2);

    // Addition is safe and always succeeds.
    let total = five_seconds + two_seconds;
    println!("Total: {:?}", total);

    // Subtraction panics if the result is negative.
    // let negative = two_seconds - five_seconds; // PANIC: attempt to subtract with overflow

    // Use checked_sub to handle negative results gracefully.
    let safe_diff = two_seconds.checked_sub(five_seconds);
    match safe_diff {
        Some(d) => println!("Difference: {:?}", d),
        None => println!("Second duration is larger; result would be negative."),
    }
}
```

Convention aside: prefer `Duration::from_secs` and `Duration::from_millis` over `Duration::new` unless you need nanosecond precision. `Duration::new` takes both seconds and nanoseconds, which makes the call site verbose. The `from_*` constructors are clearer and the compiler optimizes them identically. If you are converting from floating-point seconds, use `Duration::from_secs_f64`, but be aware that floating-point arithmetic loses precision. For high-precision timing, stick to integer-based constructors.

`Duration` has a maximum value. Adding two large durations can overflow. The `+` operator wraps on overflow in debug builds and panics in release builds? No, `Duration` addition saturates or wraps depending on the implementation? Actually, `Duration` implements `Add` using checked arithmetic that panics on overflow in debug mode and wraps in release mode? Let's verify. `Duration::checked_add` exists. The `+` operator on `Duration` uses `checked_add` and panics on overflow. This is consistent with Rust's arithmetic rules for checked operations. If you need non-panicking addition, use `checked_add`.

Don't assume durations are infinite. Use `checked_add` when combining user-provided values. The compiler protects you from overflow, but only if you ask for it.

## SystemTime: the wall clock

`SystemTime` represents a point in time on the system clock. It corresponds to the wall clock, like the time shown in the corner of your screen. You use `SystemTime` for logging, scheduling, and storing timestamps in databases.

`SystemTime` does not implement the `Add` or `Sub` traits. You cannot write `time + duration`. Instead, you must use `checked_add` and `checked_sub`. These methods return `Option<SystemTime>` and `Option<Duration>` respectively. The return type is `Option` because the operation can fail. The system clock has limits. Adding a duration might exceed the maximum representable time. Subtracting might go before the minimum. `checked_add` returns `None` in these cases.

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

fn main() {
    let now = SystemTime::now();
    let five_minutes = Duration::from_secs(300);

    // SystemTime does not support the + operator.
    // let future = now + five_minutes; // ERROR: no implementation for `SystemTime + Duration`

    // Use checked_add to add a duration to a timestamp.
    let future = now.checked_add(five_minutes).expect("Time overflow: duration too large");
    println!("Future time: {:?}", future);

    // Subtraction returns a Result because the difference might be negative or invalid.
    // checked_sub returns Option<Duration>.
    let diff = future.checked_sub(now).expect("Times are too far apart");
    println!("Difference: {:?}", diff);
}
```

`SystemTime` can jump. The operating system adjusts the clock periodically using NTP (Network Time Protocol). The clock can move forward or backward. If you measure elapsed time using `SystemTime`, your measurement might be wrong. The clock might have been adjusted between your start and end readings. Never use `SystemTime` to measure elapsed time or for timeouts. Use `Instant` for that.

`SystemTime` subtraction returns a `Result` in the `sub` method, but `checked_sub` returns `Option`. The `Result` variant includes an error type that explains why the subtraction failed. In practice, `checked_sub` is easier to work with. It returns `None` if the subtraction is invalid.

The wall clock lies to you during NTP adjustments. Use `SystemTime` for human-readable timestamps, not for measuring intervals.

## Instant: the stopwatch

`Instant` represents a point in time on a monotonic clock. It is like a stopwatch. The clock starts at some arbitrary point and only moves forward. It never jumps backward. You use `Instant` for measuring elapsed time, setting timeouts, and calculating performance metrics.

`Instant` supports `checked_add` and `checked_sub`. It also provides `duration_since`, which returns the time elapsed since the instant. `duration_since` returns a `Result` because the other instant might be in the future. If the argument is in the future, `duration_since` returns an error.

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

fn main() {
    let start = Instant::now();

    // Simulate some work.
    std::thread::sleep(Duration::from_millis(100));

    let end = Instant::now();

    // duration_since returns the elapsed time.
    // It returns a Result because the argument might be in the future.
    let elapsed = end.duration_since(start).expect("Time went backwards");
    println!("Elapsed: {:?}", elapsed);

    // Instant supports checked_add for calculating deadlines.
    let deadline = start.checked_add(Duration::from_secs(10)).expect("Deadline overflow");
    println!("Deadline: {:?}", deadline);
}
```

Convention aside: use `Instant::now()` for timers and `SystemTime::now()` for logs. This distinction is a community standard. Code reviewers will flag `SystemTime` usage in performance-critical paths. `Instant` is monotonic and immune to clock adjustments. `SystemTime` is subject to NTP jumps. Mixing them leads to subtle bugs where a timeout fires early or late because the clock shifted.

`Instant` values are not comparable across processes. An `Instant` from one process has no meaning in another. Do not serialize `Instant` and expect it to work elsewhere. Use `SystemTime` for timestamps that need to be shared or stored.

The stopwatch never lies. Pick `Instant` for timers, `SystemTime` for logs.

## Calendar math requires chrono

`Duration` and `SystemTime` handle seconds and nanoseconds. They cannot handle months, years, or leap days. Adding one month to a date is ambiguous. Does it mean 30 days? 31 days? The next month regardless of length? Rust's standard library does not guess. It leaves calendar arithmetic to crates.

The `chrono` crate provides types for calendar dates and times. It supports adding months, years, and handling timezones. If you need to calculate a birthday, a billing cycle, or a date range, use `chrono`.

```rust
// This code requires the chrono crate.
// Add `chrono = "0.4"` to Cargo.toml.
use chrono::{DateTime, Duration as ChronoDuration, Utc};

fn main() {
    let now: DateTime<Utc> = Utc::now();

    // chrono::Duration supports months and years.
    let one_month = ChronoDuration::months(1);
    let next_month = now + one_month;

    println!("Now: {}", now);
    println!("Next month: {}", next_month);
}
```

`chrono` uses its own `Duration` type, which is distinct from `std::time::Duration`. `chrono::Duration` can represent months and years. `std::time::Duration` cannot. The two types do not mix. Convert between them when crossing the boundary.

Calendar math belongs in `chrono`. `Duration` stops at nanoseconds.

## Pitfalls and compiler errors

Time arithmetic in Rust exposes several common pitfalls. The compiler catches many of them, but some require careful handling.

If you try to add a `Duration` to a `SystemTime` using the `+` operator, the compiler rejects you with `E0277` (trait bound not satisfied). The error message says there is no implementation for `SystemTime + Duration`. This is a feature. The compiler forces you to use `checked_add`, which makes you think about overflow.

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

fn main() {
    let now = SystemTime::now();
    let dur = Duration::from_secs(10);

    // ERROR: E0277: the trait bound `SystemTime: std::ops::Add<Duration>` is not satisfied
    let future = now + dur;
}
```

If you subtract two `Duration` values and the result is negative, the program panics. This panic happens at runtime. Use `checked_sub` to avoid it. The panic message is clear: "attempt to subtract with overflow". In production code, panics are undesirable. Handle the `None` case from `checked_sub`.

`SystemTime` subtraction can fail if the times are too far apart. The `sub` method returns a `Result`. The `checked_sub` method returns `Option`. If you ignore the `Result` or `Option`, you risk a panic or a logic error. Always handle the failure case.

`SystemTime` can go backwards. If you store a timestamp and compare it later, the comparison might be wrong if the clock was adjusted. Use `Instant` for comparisons that depend on elapsed time.

Don't guess at time arithmetic. Use `checked_add` and handle the `None`. The compiler protects you from overflow, but only if you ask for it.

## Decision: choosing the right type

Rust provides multiple types for time. Choosing the right one depends on your use case. Use the parallel structure below to decide.

Use `Duration` when you need a span of time, such as a timeout, interval, or elapsed time. Use `Duration` for arithmetic between spans. Use `checked_add` and `checked_sub` to handle overflow and negative results.

Use `SystemTime` when you need a wall-clock timestamp, such as for logging, storing in a database, or displaying to a user. Use `checked_add` and `checked_sub` to add or subtract durations. Handle the `Option` return value.

Use `Instant` when you need to measure elapsed time, set a timeout, or calculate performance metrics. Use `Instant` for monotonic timing that is immune to clock adjustments. Use `duration_since` to get the elapsed time.

Use `chrono` when you need calendar arithmetic, such as adding months, years, or handling leap days. Use `chrono` for timezones and date formatting. Convert to `std::time` types when interacting with the standard library.

Use `std::time::SystemTime` for interoperability with OS APIs and file metadata. Use `chrono::DateTime` for human-readable dates and times.

The compiler forces you to choose the right tool. Pick `Instant` for timers, `SystemTime` for logs, `Duration` for spans, and `chrono` for calendars.

## Where to go next

- [How to Use the chrono Crate in Rust](/en/how-to-use-the-chrono-crate-in-rust/)
- [How to Format Dates and Times in Rust](/en/how-to-format-dates-and-times-in-rust/)
- [How to Get the Current Date and Time in Rust](/en/how-to-get-the-current-date-and-time-in-rust/)