Merge branch '0.4.x' into merge_0.4.x

This commit is contained in:
Paul Dicker 2023-05-31 12:05:39 +02:00
commit 8164f9f635
15 changed files with 189 additions and 103 deletions

View File

@ -18,8 +18,12 @@ jobs:
- run: cargo fmt --check -- --color=always
- run: cargo fmt --check --manifest-path fuzz/Cargo.toml
- run: cargo clippy --color=always -- -D warnings
- run: cargo clippy --color=always --target x86_64-pc-windows-msvc -- -D warnings
- run: cargo clippy --manifest-path fuzz/Cargo.toml --color=always -- -D warnings
- run: |
cargo clippy --color=always --target x86_64-pc-windows-msvc \
-- -D warnings
- run: |
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
-- -D warnings
env:
RUSTFLAGS: "-Dwarnings"

View File

@ -45,9 +45,18 @@ jobs:
with:
toolchain: 1.56.1
- uses: Swatinem/rust-cache@v2
# run --lib and --doc to avoid the long running integration tests which are run elsewhere
- run: cargo test --lib --features unstable-locales,wasmbind,clock,serde,windows-sys --color=always -- --color=always
- run: cargo test --doc --features unstable-locales,wasmbind,clock,serde,windows-sys --color=always -- --color=always
# run --lib and --doc to avoid the long running integration tests
# which are run elsewhere
- run: |
cargo test --lib \
--features \
unstable-locales,wasmbind,clock,serde,windows-sys \
--color=always -- --color=always
- run: |
cargo test --doc \
--features \
unstable-locales,wasmbind,clock,serde,windows-sys \
--color=always -- --color=always
rust_versions:
strategy:
@ -63,7 +72,8 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: cargo check --benches
- run: cargo check --manifest-path fuzz/Cargo.toml --all-targets
# run --lib and --doc to avoid the long running integration tests which are run elsewhere
# run --lib and --doc to avoid the long running integration tests
# which are run elsewhere
- run: cargo test --lib --all-features --color=always -- --color=always
- run: cargo test --doc --all-features --color=always -- --color=always
@ -77,7 +87,13 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-hack
- uses: Swatinem/rust-cache@v2
- run: cargo hack check --feature-powerset --optional-deps serde,rkyv --skip default --skip __internal_bench --skip __doctest --skip iana-time-zone --skip pure-rust-locales
- run: |
cargo hack check --feature-powerset --optional-deps serde,rkyv \
--skip default --skip __internal_bench --skip __doctest \
--skip iana-time-zone --skip pure-rust-locales
# run using `bash` on all platforms for consistent
# line-continuation marks
shell: bash
- run: cargo test --lib --no-default-features
- run: cargo test --doc --no-default-features
@ -119,9 +135,12 @@ jobs:
with:
node-version: "12"
- run: |
set -euxo pipefail
export RUST_BACKTRACE=1
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf \
| bash --noprofile --norc
wasm-pack --version
shell: bash
- run: cargo build --target ${{ matrix.target }} --color=always
features_check_wasm:
@ -136,7 +155,10 @@ jobs:
targets: wasm32-unknown-unknown
- uses: taiki-e/install-action@cargo-hack
- uses: Swatinem/rust-cache@v2
- run: cargo hack check --feature-powerset --optional-deps serde,rkyv --skip default --skip __internal_bench --skip __doctest --skip iana-time-zone --skip pure-rust-locales
- run: |
cargo hack check --feature-powerset --optional-deps serde,rkyv \
--skip default --skip __internal_bench --skip __doctest \
--skip iana-time-zone --skip pure-rust-locales
cross-targets:
strategy:

View File

@ -3,8 +3,8 @@ cff-version: 1.2.0
message: Please cite this crate using these information.
# Version information.
date-released: 2023-05-29
version: 0.4.25
date-released: 2023-05-31
version: 0.4.26
# Project information.
abstract: Date and time library for Rust

View File

@ -10,7 +10,7 @@ categories = ["date-and-time"]
readme = "README.md"
license = "MIT OR Apache-2.0"
exclude = ["/ci/*"]
edition = "2018"
edition = "2021"
rust-version = "1.56.0"
[lib]
@ -51,7 +51,6 @@ android-tzdata = "0.1.1"
serde_json = { version = "1" }
serde_derive = { version = "1", default-features = false }
bincode = { version = "1.3.0" }
num-iter = { version = "0.1.35", default-features = false }
doc-comment = { version = "0.3" }
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]

View File

@ -1,4 +1,4 @@
[Chrono][docsrs]: Date and Time for Rust
[Chrono][docsrs]: Timezone-aware date and time handling
========================================
[![Chrono GitHub Actions][gh-image]][gh-checks]
@ -15,45 +15,65 @@
[gitter-image]: https://badges.gitter.im/chrono-rs/chrono.svg
[gitter]: https://gitter.im/chrono-rs/chrono
It aims to be a feature-complete superset of
the [time](https://github.com/rust-lang-deprecated/time) library.
In particular,
Chrono aims to provide all functionality needed to do correct operations on dates and times in the
[proleptic Gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar):
* Chrono strictly adheres to ISO 8601.
* Chrono is timezone-aware by default, with separate timezone-naive types.
* Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
* The [`DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) type is timezone-aware
by default, with separate timezone-naive types.
* Operations that may produce an invalid or ambiguous date and time return `Option` or
[`LocalResult`](https://docs.rs/chrono/latest/chrono/offset/enum.LocalResult.html).
* Configurable parsing and formatting with an `strftime` inspired date and time formatting syntax.
* The [`Local`](https://docs.rs/chrono/latest/chrono/offset/struct.Local.html) timezone works with
the current timezone of the OS.
* Types and operations are implemented to be reasonably efficient.
There were several previous attempts to bring a good date and time library to Rust,
which Chrono builds upon and should acknowledge:
Timezone data is not shipped with chrono by default to limit binary sizes. Use the companion crate
[Chrono-TZ](https://crates.io/crates/chrono-tz) or [`tzfile`](https://crates.io/crates/tzfile) for
full timezone support.
* [Initial research on
the wiki](https://github.com/rust-lang/rust-wiki-backup/blob/master/Lib-datetime.md)
* Dietrich Epp's [datetime-rs](https://github.com/depp/datetime-rs)
* Luis de Bethencourt's [rust-datetime](https://github.com/luisbg/rust-datetime)
## Documentation
See [docs.rs](https://docs.rs/chrono/latest/chrono/) for the API reference.
## Limitations
Only proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
Be very careful if you really have to deal with pre-20C dates, they can be in Julian or others.
* Only the proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
* Date types are limited to about +/- 262,000 years from the common epoch.
* Time types are limited to nanosecond accuracy.
* Leap seconds can be represented, but Chrono does not fully support them.
See [Leap Second Handling](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveTime.html#leap-second-handling).
Date types are limited in about +/- 262,000 years from the common epoch.
Time types are limited in the nanosecond accuracy.
## Crate features
[Leap seconds are supported in the representation but
Chrono doesn't try to make use of them](https://docs.rs/chrono/0.5/chrono/naive/struct.NaiveTime.html#leap-second-handling).
(The main reason is that leap seconds are not really predictable.)
Almost *every* operation over the possible leap seconds will ignore them.
Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
if you want.
Default features:
Chrono inherently does not support an inaccurate or partial date and time representation.
Any operation that can be ambiguous will return `None` in such cases.
For example, "a month later" of 2014-01-30 is not well-defined
and consequently `Utc.ymd_opt(2014, 1, 30).unwrap().with_month(2)` returns `None`.
* `alloc`: Enable features that depend on allocation (primarily string formatting)
* `std`: Enables functionality that depends on the standard library. This is a superset of `alloc`
and adds interoperation with standard library types and traits.
* `clock`: Enables reading the system time (`now`) and local timezone (`Local`).
* `wasmbind`: Interface with the JS Date API for the `wasm32` target.
Non ISO week handling is not yet supported.
For now you can use the [chrono_ext](https://crates.io/crates/chrono_ext)
crate ([sources](https://github.com/bcourtine/chrono-ext/)).
Optional features:
Advanced time zone handling is not yet supported.
For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
* `serde`: Enable serialization/deserialization via serde.
* `rkyv`: Enable serialization/deserialization via rkyv.
* `rustc-serialize`: Enable serialization/deserialization via rustc-serialize (deprecated).
* `arbitrary`: construct arbitrary instances of a type with the Arbitrary crate.
* `unstable-locales`: Enable localization. This adds various methods with a `_localized` suffix.
The implementation and API may change or even be removed in a patch release. Feedback welcome.
## Rust version requirements
The Minimum Supported Rust Version (MSRV) is currently **Rust 1.56.0**.
The MSRV is explicitly tested in CI. It may be bumped in minor releases, but this is not done
lightly.
## License
This project is licensed under either of
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
* [MIT License](https://opensource.org/licenses/MIT)
at your option.

View File

@ -866,6 +866,7 @@ fn test_parse() {
// fixed: dot plus nanoseconds
check!("", [fix!(Nanosecond)]; ); // no field set, but not an error
check!(".", [fix!(Nanosecond)]; TOO_SHORT);
check!("4", [fix!(Nanosecond)]; TOO_LONG); // never consumes `4`
check!("4", [fix!(Nanosecond), num!(Second)]; second: 4);
check!(".0", [fix!(Nanosecond)]; nanosecond: 0);
@ -888,13 +889,13 @@ fn test_parse() {
check!(".000000000547", [fix!(Nanosecond)]; nanosecond: 0);
check!(".0000000009999999999999999999999999", [fix!(Nanosecond)]; nanosecond: 0);
check!(".4🤠", [fix!(Nanosecond), lit!("🤠")]; nanosecond: 400_000_000);
check!(".", [fix!(Nanosecond)]; TOO_SHORT);
check!(".4x", [fix!(Nanosecond)]; TOO_LONG);
check!(". 4", [fix!(Nanosecond)]; INVALID);
check!(" .4", [fix!(Nanosecond)]; TOO_LONG); // no automatic trimming
// fixed: nanoseconds without the dot
check!("", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!(".", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("0", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("4", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
check!("42", [internal_fix!(Nanosecond3NoDot)]; TOO_SHORT);
@ -910,6 +911,7 @@ fn test_parse() {
check!(".421", [internal_fix!(Nanosecond3NoDot)]; INVALID);
check!("", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!(".", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("0", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("1234", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
check!("12345", [internal_fix!(Nanosecond6NoDot)]; TOO_SHORT);
@ -923,6 +925,7 @@ fn test_parse() {
check!(".42100", [internal_fix!(Nanosecond6NoDot)]; INVALID);
check!("", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
check!(".", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
check!("42195", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
check!("12345678", [internal_fix!(Nanosecond9NoDot)]; TOO_SHORT);
check!("421950803", [internal_fix!(Nanosecond9NoDot)]; nanosecond: 421_950_803);

View File

@ -4,8 +4,6 @@
//! A collection of parsed date and time items.
//! They can be constructed incrementally while being checked for consistency.
use core::convert::TryFrom;
use super::{ParseResult, IMPOSSIBLE, NOT_ENOUGH, OUT_OF_RANGE};
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
use crate::offset::{FixedOffset, LocalResult, Offset, TimeZone};

View File

@ -1,4 +1,4 @@
use core::{convert::TryFrom, fmt};
use core::fmt;
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
@ -12,7 +12,6 @@ use crate::OutOfRange;
///
/// It is possible to convert from a date to a month independently
/// ```
/// # use std::convert::TryFrom;
/// use chrono::prelude::*;
/// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
/// // `2019-10-28T09:10:11Z`
@ -311,8 +310,6 @@ mod month_serde {
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use super::Month;
use crate::{Datelike, OutOfRange, TimeZone, Utc};

View File

@ -5,7 +5,6 @@
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::convert::TryFrom;
use core::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign};
use core::{fmt, str};
@ -2250,10 +2249,7 @@ mod tests {
};
use crate::time_delta::TimeDelta;
use crate::{Datelike, Weekday};
use std::{
convert::{TryFrom, TryInto},
i32, u32,
};
use std::{i32, u32};
#[test]
fn diff_months() {
@ -2342,9 +2338,7 @@ mod tests {
#[test]
fn test_readme_doomsday() {
use num_iter::range_inclusive;
for y in range_inclusive(NaiveDate::MIN.year(), NaiveDate::MAX.year()) {
for y in NaiveDate::MIN.year()..=NaiveDate::MAX.year() {
// even months
let d4 = NaiveDate::from_ymd_opt(y, 4, 4).unwrap();
let d6 = NaiveDate::from_ymd_opt(y, 6, 6).unwrap();

View File

@ -5,7 +5,6 @@
#[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow;
use core::convert::TryFrom;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::{fmt, str};

View File

@ -504,8 +504,6 @@ const fn weekday_from_u32_mod7(n: u32) -> Weekday {
#[cfg(test)]
mod tests {
use num_iter::range_inclusive;
use std::convert::TryFrom;
use std::u32;
use super::weekday_from_u32_mod7;
@ -555,7 +553,7 @@ mod tests {
#[test]
fn test_of() {
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
for ordinal in range_inclusive(ordinal1, ordinal2) {
for ordinal in ordinal1..=ordinal2 {
let of = match Of::new(ordinal, flags) {
Some(of) => of,
None if !expected => continue,
@ -591,8 +589,8 @@ mod tests {
#[test]
fn test_mdf_valid() {
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
for month in range_inclusive(month1, month2) {
for day in range_inclusive(day1, day2) {
for month in month1..=month2 {
for day in day1..=day2 {
let mdf = match Mdf::new(month, day, flags) {
Some(mdf) => mdf,
None if !expected => continue,
@ -682,7 +680,7 @@ mod tests {
#[test]
fn test_of_fields() {
for &flags in FLAGS.iter() {
for ordinal in range_inclusive(1u32, 366) {
for ordinal in 1u32..=366 {
if let Some(of) = Of::new(ordinal, flags) {
assert_eq!(of.ordinal(), ordinal);
}
@ -695,7 +693,7 @@ mod tests {
fn check(flags: YearFlags, ordinal: u32) {
let of = Of::new(ordinal, flags).unwrap();
for ordinal in range_inclusive(0u32, 1024) {
for ordinal in 0u32..=1024 {
let of = of.with_ordinal(ordinal);
assert_eq!(of, Of::new(ordinal, flags));
if let Some(of) = of {
@ -733,7 +731,7 @@ mod tests {
for &flags in FLAGS.iter() {
let mut prev = Of::new(1, flags).unwrap().weekday();
for ordinal in range_inclusive(2u32, flags.ndays()) {
for ordinal in 2u32..=flags.ndays() {
let of = Of::new(ordinal, flags).unwrap();
let expected = prev.succ();
assert_eq!(of.weekday(), expected);
@ -745,8 +743,8 @@ mod tests {
#[test]
fn test_mdf_fields() {
for &flags in FLAGS.iter() {
for month in range_inclusive(1u32, 12) {
for day in range_inclusive(1u32, 31) {
for month in 1u32..=12 {
for day in 1u32..31 {
let mdf = match Mdf::new(month, day, flags) {
Some(mdf) => mdf,
None => continue,
@ -766,7 +764,7 @@ mod tests {
fn check(flags: YearFlags, month: u32, day: u32) {
let mdf = Mdf::new(month, day, flags).unwrap();
for month in range_inclusive(0u32, 16) {
for month in 0u32..=16 {
let mdf = match mdf.with_month(month) {
Some(mdf) => mdf,
None if month > 12 => continue,
@ -779,7 +777,7 @@ mod tests {
}
}
for day in range_inclusive(0u32, 1024) {
for day in 0u32..=1024 {
let mdf = match mdf.with_day(day) {
Some(mdf) => mdf,
None if day > 31 => continue,
@ -822,7 +820,7 @@ mod tests {
#[test]
fn test_of_to_mdf() {
for i in range_inclusive(0u32, 8192) {
for i in 0u32..=8192 {
if let Some(of) = Of(i).validate() {
assert!(of.to_mdf().valid());
}
@ -831,7 +829,7 @@ mod tests {
#[test]
fn test_mdf_to_of() {
for i in range_inclusive(0u32, 8192) {
for i in 0u32..=8192 {
let mdf = Mdf(i);
assert_eq!(mdf.valid(), mdf.to_of().is_some());
}
@ -839,7 +837,7 @@ mod tests {
#[test]
fn test_of_to_mdf_to_of() {
for i in range_inclusive(0u32, 8192) {
for i in 0u32..=8192 {
if let Some(of) = Of(i).validate() {
assert_eq!(of, of.to_mdf().to_of().unwrap());
}
@ -848,7 +846,7 @@ mod tests {
#[test]
fn test_mdf_to_of_to_mdf() {
for i in range_inclusive(0u32, 8192) {
for i in 0u32..=8192 {
let mdf = Mdf(i);
if mdf.valid() {
assert_eq!(mdf, mdf.to_of().unwrap().to_mdf());

View File

@ -178,6 +178,9 @@ where
T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
if let Some(span) = duration.num_nanoseconds() {
if span < 0 {
return Err(RoundingError::DurationExceedsLimit);
}
if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS {
return Err(RoundingError::TimestampExceedsLimit);
}
@ -217,6 +220,9 @@ where
T: Timelike + Add<TimeDelta, Output = T> + Sub<TimeDelta, Output = T>,
{
if let Some(span) = duration.num_nanoseconds() {
if span < 0 {
return Err(RoundingError::DurationExceedsLimit);
}
if naive.timestamp().abs() > MAX_SECONDS_TIMESTAMP_FOR_NANOS {
return Err(RoundingError::TimestampExceedsLimit);
}
@ -304,10 +310,10 @@ impl std::error::Error for RoundingError {
#[cfg(test)]
mod tests {
use super::{DurationRound, SubsecRound, TimeDelta};
use super::{DurationRound, RoundingError, SubsecRound, TimeDelta};
use crate::offset::{FixedOffset, TimeZone, Utc};
use crate::NaiveDate;
use crate::Timelike;
use crate::{NaiveDate, NaiveDateTime};
#[test]
fn test_round_subsecs() {
@ -760,4 +766,19 @@ mod tests {
"1969-12-12 12:10:00 UTC"
);
}
#[test]
fn issue1010() {
let dt = NaiveDateTime::from_timestamp_opt(-4227854320, 1678774288).unwrap();
let span = TimeDelta::microseconds(-7019067213869040);
assert_eq!(dt.duration_trunc(span), Err(RoundingError::DurationExceedsLimit));
let dt = NaiveDateTime::from_timestamp_opt(320041586, 1920103021).unwrap();
let span = TimeDelta::nanoseconds(-8923838508697114584);
assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
let dt = NaiveDateTime::from_timestamp_opt(-2621440, 0).unwrap();
let span = TimeDelta::nanoseconds(-9223372036854771421);
assert_eq!(dt.duration_round(span), Err(RoundingError::DurationExceedsLimit));
}
}

View File

@ -219,9 +219,7 @@ mod tests {
date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400)
}
use num_iter::range_inclusive;
for year in range_inclusive(NaiveDate::MIN.year(), NaiveDate::MAX.year()) {
for year in NaiveDate::MIN.year()..=NaiveDate::MAX.year() {
let jan1_year = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
assert_eq!(
jan1_year.num_days_from_ce(),

View File

@ -1,4 +1,4 @@
use core::{convert::TryFrom, fmt};
use core::fmt;
#[cfg(feature = "rkyv")]
use rkyv::{Archive, Deserialize, Serialize};
@ -14,7 +14,6 @@ use crate::OutOfRange;
/// # Example
/// ```
/// use chrono::Weekday;
/// use std::convert::TryFrom;
///
/// let monday = "Monday".parse::<Weekday>().unwrap();
/// assert_eq!(monday, Weekday::Mon);
@ -199,7 +198,6 @@ impl fmt::Debug for ParseWeekdayError {
#[cfg(test)]
mod tests {
use super::Weekday;
use std::convert::TryFrom;
#[test]
fn test_num_days_from() {

View File

@ -52,34 +52,68 @@ fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) {
}
}
/// path to Unix `date` command. Should work on most Linux and Unixes. Not the
/// path for MacOS (/bin/date) which uses a different version of `date` with
/// different arguments (so it won't run which is okay).
/// for testing only
#[allow(dead_code)]
#[cfg(not(target_os = "aix"))]
const DATE_PATH: &'static str = "/usr/bin/date";
#[allow(dead_code)]
#[cfg(target_os = "aix")]
const DATE_PATH: &'static str = "/opt/freeware/bin/date";
#[cfg(test)]
/// test helper to sanity check the date command behaves as expected
/// asserts the command succeeded
fn assert_run_date_version() {
// note environment variable `LANG`
match std::env::var_os("LANG") {
Some(lang) => eprintln!("LANG: {:?}", lang),
None => eprintln!("LANG not set"),
}
let out = process::Command::new(DATE_PATH).arg("--version").output().unwrap();
let stdout = String::from_utf8(out.stdout).unwrap();
let stderr = String::from_utf8(out.stderr).unwrap();
// note the `date` binary version
eprintln!("command: {:?} --version\nstdout: {:?}\nstderr: {:?}", DATE_PATH, stdout, stderr);
assert!(out.status.success(), "command failed: {:?} --version", DATE_PATH);
}
#[test]
#[cfg(unix)]
fn try_verify_against_date_command() {
#[cfg(not(target_os = "aix"))]
let date_path = "/usr/bin/date";
#[cfg(target_os = "aix")]
let date_path = "/opt/freeware/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.
if !path::Path::new(DATE_PATH).exists() {
eprintln!("date command {:?} not found, skipping", DATE_PATH);
return;
}
assert_run_date_version();
let mut date = NaiveDate::from_ymd_opt(1975, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap();
eprintln!(
"Run command {:?} for every hour from {} to 2077, skipping some years...",
DATE_PATH,
date.year()
);
let mut count: u64 = 0;
let mut year_at = date.year();
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);
if date.year() != year_at {
eprintln!("at year {}...", date.year());
year_at = date.year();
}
verify_against_date_command_local(DATE_PATH, date);
count += 1;
}
date += chrono::TimeDelta::hours(1);
}
eprintln!("Command {:?} was run {} times", DATE_PATH, count);
}
#[cfg(target_os = "linux")]
@ -98,6 +132,7 @@ fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTim
// Z%Z - too many ways to represent it, will most likely fail
let output = process::Command::new(path)
.env("LANG", "c")
.arg("-d")
.arg(format!(
"{}-{:02}-{:02} {:02}:{:02}:{:02}",
@ -124,15 +159,15 @@ fn verify_against_date_command_format_local(path: &'static str, dt: NaiveDateTim
#[test]
#[cfg(target_os = "linux")]
fn try_verify_against_date_command_format() {
let date_path = "/usr/bin/date";
if !path::Path::new(date_path).exists() {
// date command not found, skipping
if !path::Path::new(DATE_PATH).exists() {
eprintln!("date command {:?} not found, skipping", DATE_PATH);
return;
}
assert_run_date_version();
let mut date = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_opt(12, 11, 13).unwrap();
while date.year() < 2008 {
verify_against_date_command_format_local(date_path, date);
verify_against_date_command_format_local(DATE_PATH, date);
date += chrono::TimeDelta::days(55);
}
}