8

I can't seem to work out how to handle Unix timestamps in Rust with chrono.

I have the following code, however the naive and therefore the datetime vars are incorrect:

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

fn main() {
    println!("Hello, world!");

    let timestamp = "1627127393230".parse::<i64>().unwrap();
    let naive = NaiveDateTime::from_timestamp(timestamp, 0);
    let datetime: DateTime<Utc> = DateTime::from_utc(naive, Utc);

    println!("timestamp: {}", timestamp);
    println!("naive: {}", naive);
    println!("datetime: {}", datetime);
}

output:

❯ cargo r
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/utc`
Hello, world!
timestamp: 1627127393230
naive: +53531-08-13 23:27:10
datetime: +53531-08-13 23:27:10 UTC

when the correct date time for 1627127393230 is:

GMT: Saturday, July 24, 2021 11:49:53.230 AM

Can someone tell me what I am missing here?


Final solution:

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

pub fn convert(timestamp: i64)  -> DateTime<Utc> {
    let naive = NaiveDateTime::from_timestamp_opt(timestamp / 1000, (timestamp % 1000) as u32 * 1_000_000).unwrap();
    DateTime::<Utc>::from_utc(naive, Utc)
}


#[test]
fn test_timestamp() {
    let timestamp = 1627127393230;
    let ts = convert(timestamp);
    assert_eq!(ts.to_string(), "2021-07-24 11:49:53.230 UTC")
}

E_net4
  • 27,810
  • 13
  • 101
  • 139
utx0_
  • 1,364
  • 14
  • 24

3 Answers3

5

from_timestamp doesn't support milliseconds. You can put the milliseconds in the second parameter as nanoseconds. But you'll have to break it out of the timestamp.

Check out the substring crate or do it like this:

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

fn main() {
    let timestamp = 1627127393i64;
    let nanoseconds = 230 * 1000000;
    let datetime = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(timestamp, nanoseconds), Utc);
    println!("{}", datetime);
}

With the substring crate:

use substring::Substring;
use chrono::{DateTime, NaiveDateTime, Utc}; 

fn main() {
    let timestamp = "1627127393230";
    let nanoseconds = substring(timestamp, 11, 3).parse::<i64>().unwrap() * 1000000;
    let timestamp = substring(timestamp, 1, 10).parse::<i64>().unwrap();
    let datetime = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(timestamp, nanoseconds), Utc);
    println!("{}", datetime);
}
Mattia Righetti
  • 1,265
  • 1
  • 18
  • 31
Timothy Alexis Vass
  • 2,526
  • 2
  • 11
  • 30
  • Is there a way to handle milliseconds in chrono? – utx0_ Jul 24 '21 at 23:53
  • The second parameter of `from_timstamp` takes nanoseconds, just shift the magnitude accordingly :-) – user2722968 Jul 24 '21 at 23:55
  • 1
    `from_timestamp` was deprecated; "Deprecated since 0.4.23: use from_timestamp_opt() instead" ([source](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html#method.from_timestamp)). (Version 0.4.23 currently the latest one as of today) – ozanmuyes Mar 12 '23 at 16:44
4

Your timestamp input is

1627127393230

The current Unix timestamp is

1627169482

Notice that your timestamp is off by three orders of magnitude, but the decimals seem correct. So my guess is your input - wherever you get the value from - is including nanosecond precision in its representation.

If you know that to be true, use timestamp / 1000 and/or shuffle the remainder into the second parameter to from_timestamp.

user2722968
  • 13,636
  • 2
  • 46
  • 67
0

The accepted answer is now partially incorrect, since it mentions a deprecated method. Here's a way to do it using the latest version of Rust (1.67.1 on my system)


// Imports needed
use chrono::{DateTime, NaiveDateTime, Utc};

let ts : i64 = 123456789; // Some timestamp

let date_obj : DateTime<Utc> = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp_opt(timestamp, /* nanoseconds? */).unwrap(), Utc);
println!("My timestamp is {}, which as a date, is {}", ts, date_obj);

Don't forget to add chrono to your Cargo.toml, under the [dependencies] section. https://crates.io/crates/chrono

Note that the from_timestamp methods assume the Linux epoch, which is January 1, 1970. Windows uses a different timestamp: January 1, 1601, and Mac/Mac-derivatives use January 1, 1904, so if you're working with something in those systems you'll need to add a conversion factor to your timestamp.

Raleigh L.
  • 599
  • 2
  • 13
  • 18