首页 > 解决方案 > 如何解析自纪元以来格式为微秒的时间戳?

问题描述

我想解析这个字符串:1595879159050208。这是我尝试过的。

use chrono::prelude::*;

fn main() {
    let utc: DateTime<Utc> = Utc::now();
    let format = "%s%6f";
    let string = utc.format(format).to_string();
    println!("{}", string); // 1595879159050208
    let parsed = DateTime::parse_from_str(&string, format);
    println!("{:?}", parsed); // Err(ParseError(TooShort))
}

为什么这不起作用?

Chrono 0.4
rustc 1.41.0

标签: timerust

解决方案


您可以在解析函数文档中看到该函数在遵守内在解析宽度的同时是贪婪的,这意味着当“匹配”您的格式时,它将尝试从左到右消耗最多的字符,同时尊重最大值。由于 unix 时间戳 (%s) 没有最大字符数,您可以在源代码中看到允许的最大字符数是 usize::MAX (至少为 65535)。在此示例中,整个字符串 (1595879159050208) 被用作时间戳Tue May 12 02:50:08 50573367 UTC(未来很远),其余数字 (%6f) 无法找到。

我们可以通过在您的格式中添加一个空格来测试这一点:"%s %6f". 在这种情况下,会出现一个新错误 ( ParseError(NotEnough)),使用DateTime::parse_from_str docs可以更好地解释该错误:

请注意,此方法需要字符串中的时区。请参阅 NaiveDateTime::parse_from_str 以获取在要解析的 str 中不需要时区的版本。

所以使用 NaiveDateTime:

use chrono::prelude::*;

fn main() {
    let utc: DateTime<Utc> = Utc::now();
    let format = "%s %6f";
    let string = utc.format(format).to_string();
    println!("{}", string); // 1595902520 811515
    let parsed = NaiveDateTime::parse_from_str(&string, format);
    println!("{:?}", parsed); // Ok(2020-07-28T02:15:20.811515)
}

不幸的是,您想解析没有空格的原始字符串。为此,我认为您可以:

  • 使用标准库将字符串解析为 i64 作为 µs 并使用 NaiveDateTime::from_timestamp 从那里计算秒数/纳秒数
  • 对字符串进行切片并分别解析秒/微秒
  • 如果您不关心亚秒级精度,请将原始字符串的一部分传递给parse_from_str,只需格式化%s

使用第二个选项:

use chrono::prelude::*;

fn main() {
    let utc: DateTime<Utc> = Utc::now();
    let format = "%s%6f";
    let string = utc.format(format).to_string();
    println!("{}", string); // 1595904294646258

    let secs = string[..string.len() - 6].parse();
    let microsecs = string[string.len() - 6..].parse::<u32>();
    let parsed = match (secs, microsecs) {
        (Ok(s), Ok(us)) => NaiveDateTime::from_timestamp_opt(s, us * 1000),
        _ => None,
    };
    println!("{:?}", parsed); // Some(2020-07-28T02:44:54.646258)
}

推荐阅读