🎓 A1Learner
🔍
0 of 149 lessons completed

Rust Timestamps in Depth

A timestamp counts seconds from the epoch. Real systems also use milliseconds and nanoseconds, and the unit changes the number without changing the moment.

Seconds and the part inside one second

The builder takes whole seconds plus nanoseconds inside the last second:

let dt = DateTime::from_timestamp(1_792_659_600, 500_000_000).unwrap();
println!("moment = {}", dt);
println!("seconds = {}", dt.timestamp());
println!("subsec nanos = {}", dt.timestamp_subsec_nanos());

Milliseconds in one number

Many web systems store milliseconds. The same moment fits in one larger count:

let dt = DateTime::from_timestamp_millis(1_792_659_600_500).unwrap();
println!("moment = {}", dt);
println!("millis = {}", dt.timestamp_millis());
println!("subsec millis = {}", dt.timestamp_subsec_millis());

Fine detail can be lost

This moment has 123456789 nanoseconds inside its second. A millisecond rebuild keeps only 123 whole milliseconds:

let precise = NaiveDate::from_ymd_opt(2026, 10, 22).unwrap().and_hms_nano_opt(9, 0, 0, 123_456_789).unwrap().and_utc();
let rebuilt = DateTime::from_timestamp_millis(precise.timestamp_millis()).unwrap();
println!("precise nanos = {}", precise.timestamp_subsec_nanos());
println!("rebuilt nanos = {}", rebuilt.timestamp_subsec_nanos());

Before the epoch

Counts can be negative. A huge count can also fall outside the range:

println!("before epoch = {}", DateTime::from_timestamp(-1, 0).unwrap());
println!("huge works = {}", DateTime::from_timestamp(i64::MAX, 0).is_some());

Meet std::time

A fixed `SystemTime` can turn into a `DateTime<Utc>`. The value can also turn back:

let system = UNIX_EPOCH + Duration::from_secs(1_792_659_600);
let converted: DateTime<Utc> = DateTime::from(system);
let back: SystemTime = SystemTime::from(converted);
println!("converted = {}", converted);
println!("round trip = {}", back.duration_since(UNIX_EPOCH).unwrap().as_secs());

Example

// Cargo.toml: chrono = "0.4" (verified with chrono 0.4.45)
use chrono::{DateTime, SecondsFormat};

fn main() {
    let stamp = 1_792_659_600_123_i64;
    let moment = DateTime::from_timestamp_millis(stamp).unwrap();
    println!("stamp = {}", stamp);
    println!("moment = {}", moment.to_rfc3339_opts(SecondsFormat::Millis, true));
    println!("back in seconds = {}", moment.timestamp());
    println!("back in millis = {}", moment.timestamp_millis());
    println!("back in nanos = {:?}", moment.timestamp_nanos_opt());
}

Key points

  • Use `timestamp()` for whole seconds.
  • Use `timestamp_millis()` for whole milliseconds.
  • The part inside one second is split off.
  • Read it with `timestamp_subsec_nanos()`.
  • Use the `_opt` builders for numbers from outside your program.
  • Bad numbers can return no value.
  • A millisecond count drops finer detail.
  • A rebuild can lose microseconds and nanoseconds.
  • A nanosecond count in an `i64` covers a smaller range than `DateTime`.
  • The `_opt` form reports overflow.

In this example, one millisecond timestamp becomes a moment. The text form keeps three fraction digits. The moment then turns back into three counts. Each count uses a different unit. Each count names the same moment.

Note

A timestamp has no month or weekday.

Turn it into a `DateTime` before you ask calendar questions.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

Why can `DateTime::from_timestamp` return no value?