0

I am trying to implement a routine that reads a file that has "@timestamp" key in JSON's. I need to convert the timestamp from a format similar to "2022-01-19T18:15:36.283Z" into a epoch time (msec since Jan 1, 1970). The Rust code is as follows:

use chrono::*;
use serde_json::{Result, Value};

fn convert_to_epoch(ddd: &str) -> i64 {
    println!(">>>> {}", ddd);
    let tmp = NaiveDateTime::parse_from_str(ddd, "%Y-%m-%dT%H:%M:%S%.3fZ");
    println!(">>>> {:?}", tmp);
    let epoch_time = tmp.unwrap();
    return epoch_time.timestamp_millis();
}

fn get_ts(v: &Value) -> (bool, String) {
    if let Some(field) = v.get("@timestamp") {
        return (true, field.to_string());
    } else {
        if let Some(field) = v.get("date") {
            return (true, field.to_string());
        } else {
            if let Some(field) = v.get("timestamp") {
                return (true, field.to_string());
            } else {
                if let Some(field) = v.get("time") {
                    return (true, field.to_string());
                } else {
                    return (false, "".to_string());
                }
            }
        }
    }
}

fn parse_file() -> Result<()> {
    let filepath = "/tmp/input.txt";
    println!("######## filepath={}", filepath);
    let data = std::fs::read_to_string(filepath).expect("file not found!");
    let lines = data.lines();
    for line in lines {
        if line.starts_with('{') && line.ends_with('}') {
            let v: Value = serde_json::from_str(line)?;
            let (exists, ts) = get_ts(&v);
            if !exists {
                println!("ts not found");
            } else {
                println!("{}", ts);
            }
            let ddd = String::from(ts);
            let epoch_time = convert_to_epoch(&ddd);
            println!("{}", epoch_time);
        } else {
            println!("Found a non JSON line: {}", line);
        }
    }
    let ddd = String::from("2022-01-19T18:15:36.283Z");
    let epoch_time = convert_to_epoch(&ddd);
    println!("{:?}", epoch_time);
    Ok(())
}
fn main() {
    let _result = parse_file();
}

When I run the code as is, it panicks and prints the following:

   Compiling p1 v0.1.0 (/Users/abc/rust/p1)
    Finished dev [unoptimized + debuginfo] target(s) in 1.24s
     Running `target/debug/p1`
######## filepath=/tmp/input.txt
"2022-01-19T18:15:36.283Z"
>>>> "2022-01-19T18:15:36.283Z"
>>>> Err(ParseError(Invalid))
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseError(Invalid)', src/main.rs:8:26
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

But when I comment out lines 46, 47 and 48, the code in 53, 54 and 55 executes fine and prints the following:

   Compiling p1 v0.1.0 (/Users/abc/rust/p1)
    Finished dev [unoptimized + debuginfo] target(s) in 0.40s
     Running `target/debug/p1`
######## filepath=/tmp/input.txt
"2022-01-19T18:15:36.283Z"
"2022-01-19T18:15:36.298Z"
"2022-01-19T18:15:41.161Z"
"2022-01-19T18:15:41.164Z"
"2022-01-19T18:16:38.281Z"
>>>> 2022-01-19T18:15:36.283Z
>>>> Ok(2022-01-19T18:15:36.283)
1642616136283

As far as I can see, the code from lines 46 to 48 is the same as the code in lines 53 to 55. Invocation from lines 53 to 55 works but that from lines 46 to 48 fails.

The input.txt file contents are as follows:

{"@timestamp":"2022-01-19T18:15:36.283Z"}
{"@timestamp":"2022-01-19T18:15:36.298Z"}
{"@timestamp":"2022-01-19T18:15:41.161Z"}
{"@timestamp":"2022-01-19T18:15:41.164Z"}
{"@timestamp":"2022-01-19T18:16:38.281Z"}
  • What's in the file? Try printing the string you're passing to `convert_to_epoch()` (or inspecting it with a debugger, for that matter). Is it indeed the same? – Chayim Friedman Aug 16 '22 at 14:48

1 Answers1

0

I was able to detect and fix the issue. For some reason, the line let ddd = String::from(ts); caused ddd to have a double quote at the beginning and end of the String. When I modified the line to let ddd = String::from(ts).replace("\"", "");, it no longer crashed.

  • A lot of this can be expressed more succinctly: [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=0a9e5eb5c3018e356850add6c0f6e4b4). You shouldn't have to `.replace()` the quotes since they appear because you called `.to_string()` on a `Value` that contained a string, which will encode the value as JSON, which for strings requires quoting them. You can simply avoid doing that by getting the string stored by a `Value` via `.as_str()`. – kmdreko Sep 19 '22 at 23:03
  • Thank you so much @kmdreko for your valuable suggestions - I have learnt so much by understanding this. – user1653651 Sep 20 '22 at 20:49