如何使用Chrono将NaiveDate转换为特定的TimeZone?

25

我正在使用chrono crate在Rust中解析日期和时间。这些日期和时间来自于一个网站,在该网站上,日期和时间分别来自不同的部分。

日期以%d/%m/%Y的格式显示(例如:27/08/2018)。时间仅以小时为单位显示(例如:12、10、21等)。

我希望将这些日期时间存储为UTC,以便可以“时区无关”的方式计算从现在到给定日期时间的剩余时间。我知道这些日期时间来自哪个时区(巴黎时间)。

我从日期输入创建了一个NaiveDate(这是一个正在进行中的工作,因此尚未进行错误处理):

let naive_date = NaiveDate::parse_from_str(date, "%d/%m/%Y").unwrap()

在那个点之后,如果我有一个包含小时的字符串,最好的方法是什么来获取UTC DateTime

我对各种TimeZone/Offset特性感到困惑,不知道是否应该使用Local,还是FixedOffset然后转换为Utc


日期以以下格式显示:%d/%m/%Y(例如:27/08/2018)。时间仅为小时数(12、10、21等)。”“27/08/2018”中的小时在哪里? - Stargateur
我注意到一个不连贯之处。所有日期时间实例是否在同一个时区?请更新问题,而不是将有用的信息留在评论中。 - E net4
1
我认为我理解了这个问题。您有一个时间和日期,它们都是naive的 - 它们没有附加时区。但是,您确实知道时区是+0100,现在您想创建一个适当调整时区的DateTime<Utc>。是这样吗? - Peter Hall
@PeterHall 确切无误 - Justmaker
@Stargateur 我的意思是我想要带有时区的日期时间,但是调整为UTC。例如,假设我的日期元素是“27/08/2018”,小时元素是“12”。我知道时区是巴黎(目前UTC + 2)。因此,我想从这些信息创建一个DateTime<Utc>。如果这不清楚,请原谅... - Justmaker
显示剩余2条评论
3个回答

27

Chrono的文档可能需要改进,以便更容易找到如何完成这些任务。

假设这是您的起点:

use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};

// The date you parsed
let date = NaiveDate::from_ymd(2018, 5, 13);
// The known 1 hour time offset in seconds
let tz_offset = FixedOffset::east(1 * 3600);
// The known time
let time = NaiveTime::from_hms(17, 0, 0);
// Naive date time, with no time zone information
let datetime = NaiveDateTime::new(date, time);

您可以使用FixedOffset构造DateTime

let dt_with_tz: DateTime<FixedOffset> = tz_offset.from_local_datetime(&datetime).unwrap();

如果你需要将它转换为一个 DateTime<Utc>,可以这样做:

let dt_with_tz_utc: DateTime<Utc> = Utc.from_utc_datetime(&dt_with_tz.naive_utc());

那正是我所需要的,非常感谢你的回答。 - Justmaker
提供信息,使用chrono-tz更加舒适: pub fn create_date_time_from_paris(date: NaiveDate, time: NaiveTime) -> DateTime<Utc> { let naive_datetime = NaiveDateTime::new(date, time); let paris_time = Paris.from_local_datetime(&naive_datetime).unwrap(); paris_time.with_timezone(&Utc) } - Justmaker
@Justmaker 谢谢,我也学到了一些东西。在查看 chrono-tz 的文档时,我意识到你可以从 FixedOffset 中做同样的事情,而你已经有了它。我更新了我的答案,因为这比减去偏移量要好得多。 - Peter Hall
是的,但是那样你就需要手动处理夏令时,而chrono-tz会为你处理它 ;) - Justmaker
3
这取决于您拥有的信息。如果您知道当前的时区是UTC+0100,那么这没问题。如果您知道您在巴黎,那么使用chrono-tz可能更好。但我仍不确定如何保证为任意城市选择正确的时区? - Peter Hall
显示剩余2条评论

16

我发现了chrono-tz,觉得使用起来更加容易。例如:

pub fn create_date_time_from_paris(date: NaiveDate, time: NaiveTime) -> DateTime<Utc> {
    let naive_datetime = NaiveDateTime::new(date, time);
    let paris_time = Paris.from_local_datetime(&naive_datetime).unwrap();
    paris_time.with_timezone(&Utc)
}

1
日期和时间来自一个网站,其中日期和时间来自页面的不同部分。
以下是如何逐步解析来自不同字符串的多个值、为未解析信息提供默认值并使用Chrono内置的时区转换的示例。
关键是使用parse函数更新Parsed结构。您可以使用StrftimeItems迭代器继续使用更可读的格式字符串。
extern crate chrono;

use chrono::prelude::*;

fn example(date: &str, hour: &str) -> chrono::ParseResult<DateTime<Utc>> {
    use chrono::format::{self, strftime::StrftimeItems, Parsed};

    // Set up a struct to perform successive parsing into
    let mut p = Parsed::default();

    // Parse the date information
    format::parse(&mut p, date.trim(), StrftimeItems::new("%d/%m/%Y"))?;
    // Parse the time information and provide default values we don't parse
    format::parse(&mut p, hour.trim(), StrftimeItems::new("%H"))?;
    p.minute = Some(0);
    p.second = Some(0);

    // Convert parsed information into a DateTime in the Paris timezone
    let paris_time_zone_offset = FixedOffset::east(1 * 3600);
    let dt = p.to_datetime_with_timezone(&paris_time_zone_offset)?;

    // You can also use chrono-tz instead of hardcoding it
    // let dt = p.to_datetime_with_timezone(&chrono_tz::Europe::Paris)?;

    // Convert to UTC
    Ok(dt.with_timezone(&Utc))
}

fn main() {
    let date = "27/08/2018";
    let hour = "12";

    println!("dt = {:?}", example(date, hour)); // Ok(2018-08-27T11:00:00Z)
}

可能是 p.offset = Some(1 * 3600); 或者 format::parse(&mut p, timezone.trim(), StrftimeItems::new("%z"))?; - Stargateur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接