Convert Unix Timestamp in Rust

Rust offers two main approaches for handling Unix timestamps: the standard library's std::time module for basic operations, and the chrono crate for comprehensive datetime handling with timezones and formatting.

Using Standard Library (std::time)

Rust
use std::time::{Duration, SystemTime, UNIX_EPOCH};

fn main() {
    // Current Unix timestamp in seconds
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards")
        .as_secs();
    println!("Current timestamp: {}", now); // e.g., 1704067200

    // Current timestamp in milliseconds
    let now_ms = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("Time went backwards")
        .as_millis();
    println!("Current timestamp (ms): {}", now_ms);

    // Unix timestamp to SystemTime
    let timestamp: u64 = 1704067200;
    let datetime = UNIX_EPOCH + Duration::from_secs(timestamp);
    println!("{:?}", datetime); // SystemTime { ... }
}

Using chrono Crate (Recommended)

Add to your Cargo.toml:

Cargo.toml
[dependencies]
chrono = "0.4"
Rust
use chrono::{DateTime, TimeZone, Utc};

fn main() {
    // Unix timestamp to DateTime<Utc>
    let timestamp: i64 = 1704067200;
    let datetime = Utc.timestamp_opt(timestamp, 0)
        .single()
        .expect("Invalid timestamp");
    println!("{}", datetime); // 2024-01-01 00:00:00 UTC

    // From milliseconds
    let timestamp_ms: i64 = 1704067200000;
    let datetime_ms = Utc.timestamp_millis_opt(timestamp_ms)
        .single()
        .expect("Invalid timestamp");
    println!("{}", datetime_ms); // 2024-01-01 00:00:00 UTC

    // Current time to Unix timestamp
    let now = Utc::now();
    println!("Seconds: {}", now.timestamp());       // e.g., 1704067200
    println!("Millis: {}", now.timestamp_millis()); // e.g., 1704067200000
    println!("Nanos: {}", now.timestamp_nanos_opt().unwrap());
}

Timezone Handling with chrono-tz

Add chrono-tz for IANA timezone support:

Cargo.toml
[dependencies]
chrono = "0.4"
chrono-tz = "0.9"
Rust
use chrono::{TimeZone, Utc};
use chrono_tz::America::New_York;
use chrono_tz::Asia::Tokyo;

fn main() {
    let timestamp: i64 = 1704067200;
    let utc_time = Utc.timestamp_opt(timestamp, 0).single().unwrap();
    
    // Convert to different timezones
    let ny_time = utc_time.with_timezone(&New_York);
    let tokyo_time = utc_time.with_timezone(&Tokyo);
    
    println!("UTC:      {}", utc_time);    // 2024-01-01 00:00:00 UTC
    println!("New York: {}", ny_time);     // 2023-12-31 19:00:00 EST
    println!("Tokyo:    {}", tokyo_time);  // 2024-01-01 09:00:00 JST

    // Create time in specific timezone
    let ny_midnight = New_York
        .with_ymd_and_hms(2024, 1, 1, 0, 0, 0)
        .single()
        .expect("Invalid datetime");
    println!("NY midnight as UTC: {}", ny_midnight.with_timezone(&Utc));
    println!("NY midnight timestamp: {}", ny_midnight.timestamp()); // 1704085200
}

Formatting and Parsing

Rust
use chrono::{DateTime, TimeZone, Utc, NaiveDateTime};

fn main() {
    let timestamp: i64 = 1704067200;
    let datetime = Utc.timestamp_opt(timestamp, 0).single().unwrap();

    // Formatting with strftime-style patterns
    println!("{}", datetime.format("%Y-%m-%d"));           // 2024-01-01
    println!("{}", datetime.format("%Y-%m-%d %H:%M:%S"));  // 2024-01-01 00:00:00
    println!("{}", datetime.format("%B %d, %Y"));          // January 01, 2024
    println!("{}", datetime.to_rfc3339());                 // 2024-01-01T00:00:00+00:00
    println!("{}", datetime.to_rfc2822());                 // Mon, 1 Jan 2024 00:00:00 +0000

    // Parsing from string
    let parsed = DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z")
        .expect("Invalid RFC3339");
    println!("Parsed timestamp: {}", parsed.timestamp()); // 1704067200

    // Parsing with custom format
    let naive = NaiveDateTime::parse_from_str(
        "2024-01-01 00:00:00",
        "%Y-%m-%d %H:%M:%S"
    ).expect("Invalid format");
    let datetime_utc = Utc.from_utc_datetime(&naive);
    println!("From custom format: {}", datetime_utc.timestamp());
}

Common Pitfalls

Handling timestamp_opt() Results

chrono 0.4.23+ deprecated timestamp() in favor of timestamp_opt()which returns a LocalResult. Always handle the result properly.

use chrono::{TimeZone, Utc, LocalResult};

// ❌ Old way (deprecated)
// let dt = Utc.timestamp(1704067200, 0);

// ✅ New way - handle the result
let dt = match Utc.timestamp_opt(1704067200, 0) {
    LocalResult::Single(dt) => dt,
    LocalResult::Ambiguous(dt1, _) => dt1, // DST transition
    LocalResult::None => panic!("Invalid timestamp"),
};

// ✅ Or use single() for simple cases
let dt = Utc.timestamp_opt(1704067200, 0)
    .single()
    .expect("Invalid timestamp");

Signed vs Unsigned Timestamps

chrono uses i64 for timestamps (supporting pre-1970 dates), while std::time uses u64 Duration. Be careful when converting.

// ❌ This will panic for negative timestamps
let timestamp: i64 = -86400; // 1969-12-31
let duration = Duration::from_secs(timestamp as u64); // Panic!

// ✅ Use chrono for pre-1970 dates
use chrono::{TimeZone, Utc};
let dt = Utc.timestamp_opt(-86400, 0).single().unwrap();
println!("{}", dt); // 1969-12-31 00:00:00 UTC

Seconds vs Milliseconds

Check the magnitude of your timestamp. 10 digits = seconds, 13 digits = milliseconds.

// ❌ Wrong - treating milliseconds as seconds
let ts = 1704067200000_i64;
let dt = Utc.timestamp_opt(ts, 0).single(); // Year 55963!

// ✅ Correct - use timestamp_millis_opt
let dt = Utc.timestamp_millis_opt(ts).single().unwrap();
println!("{}", dt); // 2024-01-01 00:00:00 UTC

Edge Cases

Rust
use chrono::{TimeZone, Utc, DateTime, NaiveDateTime};

fn main() {
    // Negative timestamps (before 1970)
    let before_1970 = Utc.timestamp_opt(-86400, 0).single().unwrap();
    println!("{}", before_1970); // 1969-12-31 00:00:00 UTC

    // Maximum DateTime (year 262143)
    let max_ts: i64 = 8210298412799;
    let max_dt = Utc.timestamp_opt(max_ts, 0).single().unwrap();
    println!("Max: {}", max_dt);

    // Minimum DateTime 
    let min_ts: i64 = -8334632851200;
    let min_dt = Utc.timestamp_opt(min_ts, 0).single().unwrap();
    println!("Min: {}", min_dt);

    // Nanosecond precision
    let precise = Utc.timestamp_opt(1704067200, 123_456_789)
        .single()
        .unwrap();
    println!("Nanoseconds: {}", precise.timestamp_subsec_nanos()); // 123456789

    // Check for valid timestamp
    let invalid = Utc.timestamp_opt(i64::MAX, 0);
    if invalid.single().is_none() {
        println!("Invalid timestamp!");
    }
}

Duration Calculations

Rust
use chrono::{Duration, TimeZone, Utc};

fn main() {
    let start = Utc.timestamp_opt(1704067200, 0).single().unwrap();
    let end = Utc.timestamp_opt(1704153600, 0).single().unwrap();

    // Calculate difference
    let diff = end.signed_duration_since(start);
    println!("Seconds: {}", diff.num_seconds());   // 86400
    println!("Minutes: {}", diff.num_minutes());   // 1440
    println!("Hours: {}", diff.num_hours());       // 24
    println!("Days: {}", diff.num_days());         // 1

    // Add/subtract duration
    let tomorrow = start + Duration::days(1);
    let yesterday = start - Duration::days(1);
    println!("Tomorrow: {}", tomorrow.timestamp());
    println!("Yesterday: {}", yesterday.timestamp());

    // Add specific time units
    let later = start + Duration::hours(2) + Duration::minutes(30);
    println!("2h 30m later: {}", later);
}

Frequently Asked Questions

What crate should I use for Unix timestamps in Rust?

For basic timestamps, use std::time::SystemTime and UNIX_EPOCH. For full datetime operations, timezones, and formatting, use the chrono crate—it's the de facto standard for datetime handling in Rust.

Does Rust use seconds or milliseconds for Unix timestamps?

std::time::Duration stores time with nanosecond precision. When converting to/from Unix timestamps, you typically work with seconds via as_secs() or with subsecond precision via as_millis(), as_micros(), or as_nanos().

How do I handle timezones in Rust?

Use the chrono crate with chrono-tz for timezone support. chrono provides Utc and Local types, while chrono-tz provides all IANA timezone definitions like America/New_York.

What is the difference between SystemTime and chrono::DateTime?

SystemTime is from the standard library and represents a point in time with nanosecond precision, but has limited formatting/parsing. chrono::DateTime provides rich datetime operations, formatting, parsing, and timezone support.