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.