Рассчитайте продолжительность между сейчас и следующей полуночи, используя Chrono

Какой идиоматический способ получить продолжительность между сейчас и следующей полуночи?

У меня есть такая функция:

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??
}

Это должно сделать Duration с 1 часом, так как следующая полночь - суббота 09 декабря 2017 00:00:00 GMT-0300 (-03)

3 ответа

Решение

Прочитав документы, я наконец нашел недостающее звено: 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 часть, которая держит часовой пояс,
  • реконструирует новый DateTime указав "00:00:00" Time с and_hms,

Есть panic! в and_hms, поэтому нужно быть осторожным, чтобы указать правильное время.

Просто вычтите две даты: полночь и сейчас:

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())
}

По просьбе Матье вы можете написать что-то вроде:

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()
}

Но я не уверен, что так лучше.

Одним из способов является подсчет секунд, пропущенных до следующей полуночи, учитывая, что 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,
    )
}

Если вам нужна точность в наносекунды, вам нужно сделать еще несколько математических операций...

Другие вопросы по тегам