chrono 0.4.23

Date and time library for Rust
Documentation
use chrono::offset::TimeZone;
use chrono::Local;
use chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike};

use std::{path, process};

#[cfg(unix)]
fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) {
    let output = process::Command::new(path)
        .arg("-d")
        .arg(format!("{}-{:02}-{:02} {:02}:05:01", dt.year(), dt.month(), dt.day(), dt.hour()))
        .arg("+%Y-%m-%d %H:%M:%S %:z")
        .output()
        .unwrap();

    let date_command_str = String::from_utf8(output.stdout).unwrap();

    // The below would be preferred. At this stage neither earliest() or latest()
    // seems to be consistent with the output of the `date` command, so we simply
    // compare both.
    // let local = Local
    //     .with_ymd_and_hms(year, month, day, hour, 5, 1)
    //     // looks like the "date" command always returns a given time when it is ambiguous
    //     .earliest();

    // if let Some(local) = local {
    //     assert_eq!(format!("{}\n", local), date_command_str);
    // } else {
    //     // we are in a "Spring forward gap" due to DST, and so date also returns ""
    //     assert_eq!("", date_command_str);
    // }

    // This is used while a decision is made wheter the `date` output needs to
    // be exactly matched, or whether LocalResult::Ambigious should be handled
    // differently

    let date = NaiveDate::from_ymd_opt(dt.year(), dt.month(), dt.day()).unwrap();
    match Local.from_local_datetime(&date.and_hms_opt(dt.hour(), 5, 1).unwrap()) {
        chrono::LocalResult::Ambiguous(a, b) => assert!(
            format!("{}\n", a) == date_command_str || format!("{}\n", b) == date_command_str
        ),
        chrono::LocalResult::Single(a) => {
            assert_eq!(format!("{}\n", a), date_command_str);
        }
        chrono::LocalResult::None => {
            assert_eq!("", date_command_str);
        }
    }
}

#[test]
#[cfg(unix)]
fn try_verify_against_date_command() {
    let date_path = "/usr/bin/date";

    if !path::Path::new(date_path).exists() {
        // date command not found, skipping
        // avoid running this on macOS, which has path /bin/date
        // as the required CLI arguments are not present in the
        // macOS build.
        return;
    }

    let mut date = NaiveDate::from_ymd_opt(1975, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();

    while date.year() < 2078 {
        if (1975..=1977).contains(&date.year())
            || (2020..=2022).contains(&date.year())
            || (2073..=2077).contains(&date.year())
        {
            verify_against_date_command_local(date_path, date);
        }

        date += chrono::Duration::hours(1);
    }
}