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?