timestampinfo.fr
Tools & documentation

TIME DOCUMENTATION

Rust timestamps: SystemTime, Chrono and time

Read Unix time in Rust, convert seconds or milliseconds and format RFC 3339 dates.

Get a timestamp with the standard library

SystemTime represents system time. duration_since returns a result you must check: a date before the Epoch produces an error, not a negative duration.

use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH};

fn main() -> Result<(), SystemTimeError> {
    let elapsed = SystemTime::now().duration_since(UNIX_EPOCH)?;
    println!("Seconds: {}", elapsed.as_secs());
    println!("Milliseconds: {}", elapsed.as_millis());
    Ok(())
}

as_secs() returns a u64; as_millis(), a u128. Check the range before narrowing these types. To time an operation, prefer std::time::Instant.

Convert and format with Chrono

Add this dependency to Cargo.toml. Chrono provides the calendar dates and formatting missing from SystemTime.

[dependencies]
chrono = "0.4"
use chrono::{DateTime, SecondsFormat};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let date = DateTime::from_timestamp(1711972800, 0)
        .ok_or("Timestamp out of range")?;
    let from_ms = DateTime::from_timestamp_millis(1711972800000)
        .ok_or("Timestamp out of range")?;

    assert_eq!(date, from_ms);
    println!("{}", date.to_rfc3339_opts(SecondsFormat::Secs, true));
    // 2024-04-01T12:00:00Z
    println!("{}", date.timestamp_millis()); // 1711972800000

    let parsed = DateTime::parse_from_rfc3339("2024-04-01T14:00:00+02:00")?;
    assert_eq!(parsed.timestamp(), date.timestamp());
    Ok(())
}

The constructors return None if the date exceeds their range. The parser returns a Result. Handle these cases for user input instead of always using unwrap().

For the current instant with Chrono: let now = chrono::Utc::now();, then now.timestamp() or now.timestamp_millis().

Handle negative timestamps

In Chrono, DateTime::from_timestamp(-1, 0) corresponds to 1969-12-31T23:59:59Z. For negative milliseconds, use from_timestamp_millis directly: manual integer division may lose the fraction or round on the wrong side of zero.

Alternative: the time crate

Here is a separate program. Explicitly enable the parsing and formatting features.

[dependencies]
time = { version = "0.3", features = ["formatting", "parsing"] }
use time::{format_description::well_known::Rfc3339, OffsetDateTime};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let date = OffsetDateTime::from_unix_timestamp(1711972800)?;
    println!("{}", date.format(&Rfc3339)?);
    // 2024-04-01T12:00:00Z
    let parsed = OffsetDateTime::parse("2024-04-01T14:00:00+02:00", &Rfc3339)?;
    assert_eq!(parsed.unix_timestamp(), date.unix_timestamp());
    Ok(())
}

Choose an appropriate representation

A fixed offset does not contain the seasonal rules of a geographical time zone. With Chrono, a companion library such as chrono-tz supports IANA names such as Europe/Paris. Keep the unit in your API contract and avoid converting large values to floating-point numbers.

Check a result in the Unix converter or the ISO 8601 converter. Compare seconds, milliseconds and nanoseconds. Also see the equivalents in Go.

Official references

Rust — SystemTime · Chrono — DateTime · Chrono and time zones · time — OffsetDateTime

English version published September 26, 2026.