Convert a UNIX timestamp to Rust

The general strategy is to convert the integer to a type that's unaware of time zones, such as chrono::NaiveDateTime, then convert that time to one that's timezone-aware, such as chrono::Utc.

Sometimes you’ll find yourself with a UNIX timestamp in a CSV file or some other external data source. That integer’s a fairly straight-forward way to store the time, but it’s not very useful to a Rust programmer.

Working Code Example

For people who light to look at the code first, here’s a working program showing you how to read a timestamp from Rustusing the chrono crate, version 0.4.31:

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

fn main() {
    let ts = 1698613134;
    
    if let Some(dt) = NaiveDateTime::from_timestamp_opt(ts, 0) {
        let dt = Utc.from_utc_datetime(&dt);
        println!("{dt:?}");
    };
}

				
			

What's a UNIX timestamp?

They’re integers representing the number of seconds that have passed since midnight 1 Jan 1970 UTC.

Timestamps don’t include any time zone information, nor do they account for leap seconds.

You can generate one yourself now if you’re on a Mac or Linux computer with the following command in the terminal:

				
					$ date +%s
1698613134
				
			

Looking at the code in more detail

Let’s take a closer look at the code snippet from above.

The general strategy is to take the variable ts, representing a timestamp as an integer, then convert it to a type that knows about times (NaiveDateTime), then convert that type into something that knows about timezones (Utc).

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

fn main() {
    let ts = 1698613134;
    
    if let Some(dt) = NaiveDateTime::from_timestamp_opt(ts, 0) {
        let dt = Utc.from_utc_datetime(&dt);
        println!("{dt:?}");
    };
}

				
			

Walking through the program from top to bottom:

  • Line 1: The program starts by importing the required dependencies. It makes use of two types (NaiveDateTime & Utc) and one trait (TimeZone).

  • Lines 3-10: The business logic of the application lives within a function, main(). main is a name used to mark the beginning of the program for the operating system.

  • Line 4: This line assigns (or, more formally, “binds the name”) ts to the number 1,698,613,134. Because it’s an integer literal, Rust will use the default type of i32, unless the context requires it to be another type.

  • Line 6: There’s a lot going on in this line, so take your time. It’s broken into two halves by the equals sign. We’ll look at the right-hand side first.
    •  NaiveDateTime::from_timestamp_opt()  returns a We’ll look at the right-hand side first, which returns a value of type Option<NaiveDateTime>. The Option provides the program with the opportunity to fail (somewhat) gracefully in case an error occurs.  from_timestamp_opt takes two arguments. The first is the number of seconds, and the second is the fractional part of the second, down to nanosecond precision.
    • On the left-hand side breaks apart this value. The if block allows the program to skip processing the timestamp if a valid time wasn’t found. let introduces a pattern match, Some(dt), which will succeed when from_timestamp_opt is able to interpret its arguments as a valid timestamp.

  • Line 7: Now we know that there’s a valid time stamp, although haven’t yet associated that with any time zone awareness.  To facilitate that, our example uses the from_utc_datetime method provided by the TimeZone trait extends Utc with a method,  used for a conversion later.

Contents