使用Chrono计算现在和下一个午夜之间的持续时间

8

如何用惯用方式获取现在和下一个午夜之间的持续时间?

我有一个这样的函数:

extern crate chrono;

use chrono::prelude::*;
use time;

fn duration_until_next_midnight() -> time::Duration {
    let now = Local::now(); // Fri Dec 08 2017 23:00:00 GMT-0300 (-03)
    // ... how to continue??
}

应该创建一个1小时的Duration,因为下一个午夜是2017年12月9日周六00:00:00 GMT-0300 (-03)

3个回答

11

在仔细查阅文档之后,我终于找到了缺失的链接:Date::and_hms

因此,实际上只需要这样简单的操作:

fn main() {
    let now = Local::now();

    let tomorrow_midnight = (now + Duration::days(1)).date().and_hms(0, 0, 0);

    let duration = tomorrow_midnight.signed_duration_since(now).to_std().unwrap();

    println!("Duration between {:?} and {:?}: {:?}", now, tomorrow_midnight, duration);
}

这个想法很简单:

  • DateTime增加到明天,
  • 提取保留时区的Date部分,
  • 通过指定"00:00:00"Timeand_hms重构一个新的DateTime

and_hms中有一个panic!,因此必须小心地指定正确的时间。


let tomorrow_midnight = now + Duration::days(1); 就足够了。它将给出明天的日期,时间为00:00:00。 - azzamsa

3

只需将两个日期相减:午夜和现在:


extern crate chrono;
use chrono::prelude::*;
use std::time;

fn duration_until_next_midnight() -> time::Duration {
    let now = Local::now();
    // change to next day and reset hour to 00:00
    let midnight = (now + chrono::Duration::days(1))
        .with_hour(0).unwrap()
        .with_minute(0).unwrap()
        .with_second(0).unwrap()
        .with_nanosecond(0).unwrap();

    println!("Duration between {:?} and {:?}:", now, midnight);
    midnight.signed_duration_since(now).to_std().unwrap()
}

fn main() {
    println!("{:?}", duration_until_next_midnight())
}

根据Matthieu的要求,您可以写一些像这样的内容:

fn duration_until_next_midnight() -> Duration {
    let now = Local::now();
    // get the NaiveDate of tomorrow
    let midnight_naivedate = (now + chrono::Duration::days(1)).naive_utc().date();
    // create a NaiveDateTime from it
    let midnight_naivedatetime = NaiveDateTime::new(midnight_naivedate, NaiveTime::from_hms(0, 0, 0));
    // get the local DateTime of midnight
    let midnight: DateTime<Local> = DateTime::from_utc(midnight_naivedatetime, *now.offset());

    println!("Duration between {:?} and {:?}:", now, midnight);
    midnight.signed_duration_since(now).to_std().unwrap()
}

但我不确定它是否更好。

有没有一种方法可以在单个调用中设置小时/分钟/秒/纳秒?这将非常有帮助。 - Matthieu M.
@MatthieuM. 很遗憾,似乎没有办法减少这个繁文缛节:https://docs.rs/chrono/0.4.0/chrono/trait.Timelike.html - Boiethios
也许更简单的方法是提取NaiveDate,然后从中重建一个NaiveDateTime,再从这个新的NaiveDateTime和时区构建一个新的Date? - Matthieu M.
@MatthieuM。即使这似乎很困难。在我看来,这个crate缺少一些函数:我按照你的建议写了一些东西,但代码不够清晰易读。在我看来,有一个 impl From<Date> for DateTime 就好了,甚至是 DateTime::new(date: Date, time: Time) 更好。 - Boiethios
@MatthieuM。抱歉,我没有看到你的回答。 - Boiethios
显示剩余5条评论

2

一种方法是计算距离下一个午夜还有多少秒,记住time::Tm考虑了夏令时和时区的影响:

tm_utcoff: i32

标识用于计算此分解时间值的时区,包括任何夏令时调整。这是相对于UTC向东的秒数。例如,对于美国太平洋夏令时,该值为-7 * 60 * 60 = -25200。

extern crate time;
use std::time::Duration;

fn duration_until_next_midnight() -> Duration {
    let tnow = time::now();

    Duration::new(
        (86400 - tnow.to_timespec().sec % 86400 - 
        i64::from(tnow.tm_utcoff)) as u64,
        0,
    )
}

如果您想要纳秒级精度,您需要进行更多的数学计算...

我可能会使用一些别名来提高可读性,例如 let seconds_in_a_day = 24 * 3600 - Boiethios

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