mirror of
https://github.com/chronotope/chrono.git
synced 2025-10-02 15:26:12 +00:00
Merge branch '0.4.x'
This commit is contained in:
commit
8cd48b4796
7
.github/workflows/codecov.yml
vendored
7
.github/workflows/codecov.yml
vendored
@ -12,12 +12,13 @@ jobs:
|
|||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Rust
|
# nightly is required for --doctests, see cargo-llvm-cov#2
|
||||||
run: rustup update stable
|
- name: Install Rust (nightly)
|
||||||
|
run: rustup update nightly
|
||||||
- name: Install cargo-llvm-cov
|
- name: Install cargo-llvm-cov
|
||||||
uses: taiki-e/install-action@cargo-llvm-cov
|
uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
- name: Generate code coverage
|
- name: Generate code coverage
|
||||||
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
|
run: cargo +nightly llvm-cov --all-features --workspace --lcov --doctests --output-path lcov.info
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
env:
|
env:
|
||||||
|
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@ -17,12 +17,16 @@ jobs:
|
|||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo fmt --check -- --color=always
|
- run: cargo fmt --check -- --color=always
|
||||||
- run: cargo fmt --check --manifest-path fuzz/Cargo.toml
|
- run: cargo fmt --check --manifest-path fuzz/Cargo.toml
|
||||||
|
- run: cargo fmt --check --manifest-path bench/Cargo.toml
|
||||||
- run: |
|
- run: |
|
||||||
cargo clippy --all-features --all-targets --color=always \
|
cargo clippy --all-features --all-targets --color=always \
|
||||||
-- -D warnings
|
-- -D warnings
|
||||||
- run: |
|
- run: |
|
||||||
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
|
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
|
||||||
-- -D warnings
|
-- -D warnings
|
||||||
|
- run: |
|
||||||
|
cargo clippy --manifest-path bench/Cargo.toml --color=always \
|
||||||
|
-- -D warnings
|
||||||
env:
|
env:
|
||||||
RUSTFLAGS: "-Dwarnings"
|
RUSTFLAGS: "-Dwarnings"
|
||||||
|
|
||||||
|
55
.github/workflows/test.yml
vendored
55
.github/workflows/test.yml
vendored
@ -6,22 +6,10 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
timezones_linux:
|
timezones:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
tz: ["ACST-9:30", "EST4", "UTC0", "Asia/Katmandu"]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- run: cargo test --all-features --color=always -- --color=always
|
|
||||||
|
|
||||||
timezones_other:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [macos-latest, windows-latest]
|
|
||||||
tz: ["ACST-9:30", "EST4", "UTC0", "Asia/Katmandu"]
|
tz: ["ACST-9:30", "EST4", "UTC0", "Asia/Katmandu"]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
@ -67,7 +55,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.rust_version }}
|
toolchain: ${{ matrix.rust_version }}
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cargo check --benches
|
- run: cargo check --manifest-path bench/Cargo.toml --benches
|
||||||
- run: cargo check --manifest-path fuzz/Cargo.toml --all-targets
|
- run: cargo check --manifest-path fuzz/Cargo.toml --all-targets
|
||||||
# run --lib and --doc to avoid the long running integration tests
|
# run --lib and --doc to avoid the long running integration tests
|
||||||
# which are run elsewhere
|
# which are run elsewhere
|
||||||
@ -119,7 +107,6 @@ jobs:
|
|||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
target:
|
target:
|
||||||
[
|
[
|
||||||
wasm32-wasi,
|
|
||||||
wasm32-unknown-emscripten,
|
wasm32-unknown-emscripten,
|
||||||
aarch64-apple-ios,
|
aarch64-apple-ios,
|
||||||
aarch64-linux-android,
|
aarch64-linux-android,
|
||||||
@ -145,7 +132,6 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
with:
|
with:
|
||||||
targets: wasm32-unknown-unknown
|
targets: wasm32-unknown-unknown
|
||||||
- uses: taiki-e/install-action@cargo-hack
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
- uses: jetli/wasm-pack-action@v0.4.0
|
- uses: jetli/wasm-pack-action@v0.4.0
|
||||||
@ -153,6 +139,27 @@ jobs:
|
|||||||
# with the host system.
|
# with the host system.
|
||||||
- run: TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind
|
- run: TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind
|
||||||
|
|
||||||
|
test_wasi:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
target:
|
||||||
|
- wasm32-wasi
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: wasm32-wasi
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
- run: cargo install cargo-wasi
|
||||||
|
- uses: mwilliamson/setup-wasmtime-action@v2
|
||||||
|
with:
|
||||||
|
wasmtime-version: "12.0.1"
|
||||||
|
# We can't use `--all-features` because `rustc-serialize` doesn't support
|
||||||
|
# `wasm32-wasi`.
|
||||||
|
- run: cargo wasi test --features=serde,unstable-locales --color=always -- --color=always
|
||||||
|
|
||||||
cross-targets:
|
cross-targets:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
@ -165,6 +172,20 @@ jobs:
|
|||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- run: cross check --target ${{ matrix.target }}
|
- run: cross check --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
cross-tests:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: cargo install cross
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
- run: cross test --lib --all-features --target i686-unknown-linux-gnu --color=always
|
||||||
|
- run: cross test --doc --all-features --target i686-unknown-linux-gnu --color=always
|
||||||
|
- run: cross test --lib --all-features --target i686-unknown-linux-musl --color=always
|
||||||
|
- run: cross test --doc --all-features --target i686-unknown-linux-musl --color=always
|
||||||
|
|
||||||
check-docs:
|
check-docs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -3,8 +3,8 @@ cff-version: 1.2.0
|
|||||||
message: Please cite this crate using these information.
|
message: Please cite this crate using these information.
|
||||||
|
|
||||||
# Version information.
|
# Version information.
|
||||||
date-released: 2023-08-29
|
date-released: 2023-09-07
|
||||||
version: 0.4.27
|
version: 0.4.30
|
||||||
|
|
||||||
# Project information.
|
# Project information.
|
||||||
abstract: Date and time library for Rust
|
abstract: Date and time library for Rust
|
||||||
|
15
Cargo.toml
15
Cargo.toml
@ -25,13 +25,11 @@ std = []
|
|||||||
clock = ["std", "winapi", "iana-time-zone", "android-tzdata"]
|
clock = ["std", "winapi", "iana-time-zone", "android-tzdata"]
|
||||||
wasmbind = ["wasm-bindgen", "js-sys"]
|
wasmbind = ["wasm-bindgen", "js-sys"]
|
||||||
unstable-locales = ["pure-rust-locales", "alloc"]
|
unstable-locales = ["pure-rust-locales", "alloc"]
|
||||||
__internal_bench = ["criterion"]
|
__internal_bench = []
|
||||||
__doctest = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.99", default-features = false, optional = true }
|
serde = { version = "1.0.99", default-features = false, optional = true }
|
||||||
pure-rust-locales = { version = "0.6", optional = true }
|
pure-rust-locales = { version = "0.6", optional = true }
|
||||||
criterion = { version = "0.4.0", optional = true }
|
|
||||||
rkyv = { version = "0.7", optional = true }
|
rkyv = { version = "0.7", optional = true }
|
||||||
arbitrary = { version = "1.0.0", features = ["derive"], optional = true }
|
arbitrary = { version = "1.0.0", features = ["derive"], optional = true }
|
||||||
|
|
||||||
@ -55,7 +53,6 @@ android-tzdata = { version = "0.1.1", optional = true }
|
|||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
serde_derive = { version = "1", default-features = false }
|
serde_derive = { version = "1", default-features = false }
|
||||||
bincode = { version = "1.3.0" }
|
bincode = { version = "1.3.0" }
|
||||||
doc-comment = { version = "0.3" }
|
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]
|
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dev-dependencies]
|
||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
@ -66,13 +63,3 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
|
|
||||||
[package.metadata.playground]
|
[package.metadata.playground]
|
||||||
features = ["serde"]
|
features = ["serde"]
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "chrono"
|
|
||||||
required-features = ["__internal_bench"]
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "serde"
|
|
||||||
required-features = ["__internal_bench", "serde"]
|
|
||||||
harness = false
|
|
||||||
|
26
bench/Cargo.toml
Normal file
26
bench/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "benches"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# Even as a `dev-dependency` Criterion and its dependencies can affect the MSRV of chrono.
|
||||||
|
# But not when it lives in a separate crate :-).
|
||||||
|
# See https://github.com/chronotope/chrono/pull/1104.
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "benches"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono = { path = "..", features = ["__internal_bench", "serde"] }
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "chrono"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "serde"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.5.0"
|
||||||
|
serde_json = "1"
|
@ -1,5 +1,4 @@
|
|||||||
//! Benchmarks for chrono that just depend on std
|
//! Benchmarks for chrono that just depend on std
|
||||||
#![cfg(feature = "__internal_bench")]
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
|
|
@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "__internal_bench")]
|
|
||||||
|
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
1
bench/src/lib.rs
Normal file
1
bench/src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
// This file only exists to make `benches` a valid crate.
|
@ -660,10 +660,24 @@ impl DateTime<FixedOffset> {
|
|||||||
/// and returns a new [`DateTime`] instance with the parsed timezone as the [`FixedOffset`].
|
/// and returns a new [`DateTime`] instance with the parsed timezone as the [`FixedOffset`].
|
||||||
///
|
///
|
||||||
/// RFC 2822 is the internet message standard that specifies the representation of times in HTTP
|
/// RFC 2822 is the internet message standard that specifies the representation of times in HTTP
|
||||||
/// and email headers.
|
/// and email headers. It is the 2001 revision of RFC 822, and is itself revised as RFC 5322 in
|
||||||
|
/// 2008.
|
||||||
///
|
///
|
||||||
/// The RFC 2822 standard allows arbitrary intermixed whitespace.
|
/// # Support for the obsolete date format
|
||||||
/// See [RFC 2822 Appendix A.5]
|
///
|
||||||
|
/// - A 2-digit year is interpreted to be a year in 1950-2049.
|
||||||
|
/// - The standard allows comments and whitespace between many of the tokens. See [4.3] and
|
||||||
|
/// [Appendix A.5]
|
||||||
|
/// - Single letter 'military' time zone names are parsed as a `-0000` offset.
|
||||||
|
/// They were defined with the wrong sign in RFC 822 and corrected in RFC 2822. But because
|
||||||
|
/// the meaning is now ambiguous, the standard says they should be be considered as `-0000`
|
||||||
|
/// unless there is out-of-band information confirming their meaning.
|
||||||
|
/// The exception is `Z`, which remains identical to `+0000`.
|
||||||
|
///
|
||||||
|
/// [4.3]: https://www.rfc-editor.org/rfc/rfc2822#section-4.3
|
||||||
|
/// [Appendix A.5]: https://www.rfc-editor.org/rfc/rfc2822#appendix-A.5
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use chrono::{DateTime, FixedOffset, TimeZone};
|
/// # use chrono::{DateTime, FixedOffset, TimeZone};
|
||||||
@ -672,8 +686,6 @@ impl DateTime<FixedOffset> {
|
|||||||
/// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()
|
/// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
///
|
|
||||||
/// [RFC 2822 Appendix A.5]: https://www.rfc-editor.org/rfc/rfc2822#appendix-A.5
|
|
||||||
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
|
||||||
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
|
@ -311,6 +311,7 @@ fn ymdhms_nano(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// local helper function to easily create a DateTime<Utc>
|
// local helper function to easily create a DateTime<Utc>
|
||||||
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
||||||
fn ymdhms_utc(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Utc> {
|
fn ymdhms_utc(year: i32, month: u32, day: u32, hour: u32, min: u32, sec: u32) -> DateTime<Utc> {
|
||||||
Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap()
|
Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap()
|
||||||
}
|
}
|
||||||
@ -818,6 +819,7 @@ fn test_datetime_from_str() {
|
|||||||
.unwrap())
|
.unwrap())
|
||||||
);
|
);
|
||||||
assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
|
assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
|
||||||
|
assert!("2015-02-18T23:16:9.15øøø".parse::<DateTime<Utc>>().is_err());
|
||||||
|
|
||||||
// no test for `DateTime<Local>`, we cannot verify that much.
|
// no test for `DateTime<Local>`, we cannot verify that much.
|
||||||
}
|
}
|
||||||
@ -915,7 +917,7 @@ fn test_parse_datetime_utc() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_utc_datetime_from_str() {
|
fn test_parse_from_str() {
|
||||||
let edt = FixedOffset::east_opt(570 * 60).unwrap();
|
let edt = FixedOffset::east_opt(570 * 60).unwrap();
|
||||||
let edt0 = FixedOffset::east_opt(0).unwrap();
|
let edt0 = FixedOffset::east_opt(0).unwrap();
|
||||||
let wdt = FixedOffset::west_opt(10 * 3600).unwrap();
|
let wdt = FixedOffset::west_opt(10 * 3600).unwrap();
|
||||||
@ -930,11 +932,7 @@ fn test_utc_datetime_from_str() {
|
|||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"),
|
DateTime::<Utc>::parse_from_str("0", "%s").unwrap(),
|
||||||
Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
DateTime::<FixedOffset>::parse_from_str("0", "%s").unwrap(),
|
|
||||||
NaiveDateTime::from_timestamp_opt(0, 0).unwrap().and_utc().fixed_offset()
|
NaiveDateTime::from_timestamp_opt(0, 0).unwrap().and_utc().fixed_offset()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -978,53 +976,6 @@ fn test_utc_datetime_from_str() {
|
|||||||
// no test for `DateTime<Local>`, we cannot verify that much.
|
// no test for `DateTime<Local>`, we cannot verify that much.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_utc_datetime_from_str_with_spaces() {
|
|
||||||
let dt = ymdhms_utc(2013, 8, 9, 23, 54, 35);
|
|
||||||
// with varying spaces - should succeed
|
|
||||||
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S "), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35 ", " %b %d %Y %H:%M:%S "), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt),);
|
|
||||||
assert_eq!(
|
|
||||||
Utc.datetime_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "),
|
|
||||||
Ok(dt),
|
|
||||||
);
|
|
||||||
assert_eq!(Utc.datetime_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt),);
|
|
||||||
assert_eq!(Utc.datetime_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt),);
|
|
||||||
// with varying spaces - should fail
|
|
||||||
// leading whitespace in format
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S").is_err());
|
|
||||||
// trailing whitespace in format
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err());
|
|
||||||
// extra mid-string whitespace in format
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
|
|
||||||
// mismatched leading whitespace
|
|
||||||
assert!(Utc.datetime_from_str("\tAug 09 2013 23:54:35", "\n%b %d %Y %H:%M:%S").is_err());
|
|
||||||
// mismatched trailing whitespace
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n").is_err());
|
|
||||||
// mismatched mid-string whitespace
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S").is_err());
|
|
||||||
// trailing whitespace in format
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err());
|
|
||||||
// trailing whitespace (newline) in format
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
|
|
||||||
// leading space in data
|
|
||||||
assert!(Utc.datetime_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
|
|
||||||
// trailing space in data
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err());
|
|
||||||
// trailing tab in data
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err());
|
|
||||||
// mismatched newlines
|
|
||||||
assert!(Utc.datetime_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
|
|
||||||
// trailing literal in data
|
|
||||||
assert!(Utc.datetime_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_parse_from_str() {
|
fn test_datetime_parse_from_str() {
|
||||||
let dt = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35);
|
let dt = ymdhms(&FixedOffset::east_opt(-9 * 60 * 60).unwrap(), 2013, 8, 9, 23, 54, 35);
|
||||||
@ -1218,8 +1169,11 @@ fn test_subsecond_part() {
|
|||||||
assert_eq!(1234567, datetime.timestamp_subsec_nanos());
|
assert_eq!(1234567, datetime.timestamp_subsec_nanos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some targets, such as `wasm32-wasi`, have a problematic definition of `SystemTime`, such as an
|
||||||
|
// `i32` (year 2035 problem), or an `u64` (no values before `UNIX-EPOCH`).
|
||||||
|
// See https://github.com/rust-lang/rust/issues/44394.
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", not(all(target_arch = "wasm32", target_os = "wasi"))))]
|
||||||
fn test_from_system_time() {
|
fn test_from_system_time() {
|
||||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
@ -18,14 +18,14 @@
|
|||||||
//! # Example
|
//! # Example
|
||||||
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
|
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
|
||||||
#![cfg_attr(feature = "std", doc = "```rust")]
|
#![cfg_attr(feature = "std", doc = "```rust")]
|
||||||
//! use chrono::{TimeZone, Utc};
|
//! use chrono::{NaiveDateTime, TimeZone, Utc};
|
||||||
//!
|
//!
|
||||||
//! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap();
|
//! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap();
|
||||||
//!
|
//!
|
||||||
//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S"));
|
//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S"));
|
||||||
//! assert_eq!(formatted, "2020-11-10 00:01:32");
|
//! assert_eq!(formatted, "2020-11-10 00:01:32");
|
||||||
//!
|
//!
|
||||||
//! let parsed = Utc.datetime_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?;
|
//! let parsed = NaiveDateTime::parse_from_str(&formatted, "%Y-%m-%d %H:%M:%S")?.and_utc();
|
||||||
//! assert_eq!(parsed, date_time);
|
//! assert_eq!(parsed, date_time);
|
||||||
//! # Ok::<(), chrono::ParseError>(())
|
//! # Ok::<(), chrono::ParseError>(())
|
||||||
//! ```
|
//! ```
|
||||||
@ -451,7 +451,7 @@ const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
|
|||||||
|
|
||||||
// this implementation is here only because we need some private code from `scan`
|
// this implementation is here only because we need some private code from `scan`
|
||||||
|
|
||||||
/// Parsing a `str` into a `Weekday` uses the format [`%W`](./format/strftime/index.html).
|
/// Parsing a `str` into a `Weekday` uses the format [`%A`](./format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -487,7 +487,7 @@ impl FromStr for Weekday {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsing a `str` into a `Month` uses the format [`%W`](./format/strftime/index.html).
|
/// Parsing a `str` into a `Month` uses the format [`%B`](./format/strftime/index.html).
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -614,7 +614,7 @@ fn parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult
|
|||||||
Ok(_) => return Err(NOT_ENOUGH),
|
Ok(_) => return Err(NOT_ENOUGH),
|
||||||
};
|
};
|
||||||
s = s.trim_start();
|
s = s.trim_start();
|
||||||
let (s, offset) = if s.len() >= 3 && "UTC".eq_ignore_ascii_case(&s[..3]) {
|
let (s, offset) = if s.len() >= 3 && "UTC".as_bytes().eq_ignore_ascii_case(&s.as_bytes()[..3]) {
|
||||||
(&s[3..], 0)
|
(&s[3..], 0)
|
||||||
} else {
|
} else {
|
||||||
scan::timezone_offset(s, scan::consume_colon_maybe, true, false, true)?
|
scan::timezone_offset(s, scan::consume_colon_maybe, true, false, true)?
|
||||||
@ -626,7 +626,7 @@ fn parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::format::*;
|
use crate::format::*;
|
||||||
use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc};
|
use crate::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Timelike, Utc};
|
||||||
|
|
||||||
macro_rules! parsed {
|
macro_rules! parsed {
|
||||||
($($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
|
($($k:ident: $v:expr),*) => (#[allow(unused_mut)] {
|
||||||
@ -1733,7 +1733,10 @@ mod tests {
|
|||||||
assert_eq!(dt.format(RFC850_FMT).to_string(), "Sunday, 06-Nov-94 08:49:37 GMT");
|
assert_eq!(dt.format(RFC850_FMT).to_string(), "Sunday, 06-Nov-94 08:49:37 GMT");
|
||||||
|
|
||||||
// Check that it parses correctly
|
// Check that it parses correctly
|
||||||
assert_eq!(Ok(dt), Utc.datetime_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT));
|
assert_eq!(
|
||||||
|
NaiveDateTime::parse_from_str("Sunday, 06-Nov-94 08:49:37 GMT", RFC850_FMT),
|
||||||
|
Ok(dt.naive_utc())
|
||||||
|
);
|
||||||
|
|
||||||
// Check that the rest of the weekdays parse correctly (this test originally failed because
|
// Check that the rest of the weekdays parse correctly (this test originally failed because
|
||||||
// Sunday parsed incorrectly).
|
// Sunday parsed incorrectly).
|
||||||
@ -1765,7 +1768,7 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for val in &testdates {
|
for val in &testdates {
|
||||||
assert_eq!(Ok(val.0), Utc.datetime_from_str(val.1, RFC850_FMT));
|
assert_eq!(NaiveDateTime::parse_from_str(val.1, RFC850_FMT), Ok(val.0.naive_utc()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let test_dates_fail = [
|
let test_dates_fail = [
|
||||||
@ -1784,7 +1787,7 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for val in &test_dates_fail {
|
for val in &test_dates_fail {
|
||||||
assert!(Utc.datetime_from_str(val, RFC850_FMT).is_err());
|
assert!(NaiveDateTime::parse_from_str(val, RFC850_FMT).is_err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,11 +909,20 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "unstable-locales")]
|
#[cfg(all(feature = "unstable-locales", target_pointer_width = "64"))]
|
||||||
fn test_type_sizes() {
|
fn test_type_sizes() {
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
assert_eq!(size_of::<Item>(), 24);
|
assert_eq!(size_of::<Item>(), 24);
|
||||||
assert_eq!(size_of::<StrftimeItems>(), 56);
|
assert_eq!(size_of::<StrftimeItems>(), 56);
|
||||||
assert_eq!(size_of::<Locale>(), 2);
|
assert_eq!(size_of::<Locale>(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(all(feature = "unstable-locales", target_pointer_width = "32"))]
|
||||||
|
fn test_type_sizes() {
|
||||||
|
use core::mem::size_of;
|
||||||
|
assert_eq!(size_of::<Item>(), 12);
|
||||||
|
assert_eq!(size_of::<StrftimeItems>(), 28);
|
||||||
|
assert_eq!(size_of::<Locale>(), 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
151
src/lib.rs
151
src/lib.rs
@ -1,20 +1,21 @@
|
|||||||
//! # Chrono: Date and Time for Rust
|
//! # Chrono: Date and Time for Rust
|
||||||
//!
|
//!
|
||||||
//! It aims to be a feature-complete superset of
|
|
||||||
//! the [time](https://github.com/rust-lang-deprecated/time) library.
|
//! Chrono aims to provide all functionality needed to do correct operations on dates and times in the
|
||||||
//! In particular,
|
//! [proleptic Gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar):
|
||||||
//!
|
//!
|
||||||
//! * Chrono strictly adheres to ISO 8601.
|
//! * The [`DateTime`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) type is timezone-aware
|
||||||
//! * Chrono is timezone-aware by default, with separate timezone-naive types.
|
//! by default, with separate timezone-naive types.
|
||||||
//! * Chrono is space-optimal and (while not being the primary goal) reasonably efficient.
|
//! * 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 a `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,
|
//! Timezone data is not shipped with chrono by default to limit binary sizes. Use the companion crate
|
||||||
//! which Chrono builds upon and should acknowledge:
|
//! [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)
|
|
||||||
//!
|
//!
|
||||||
//! ### Features
|
//! ### Features
|
||||||
//!
|
//!
|
||||||
@ -29,10 +30,13 @@
|
|||||||
//! and traits.
|
//! and traits.
|
||||||
//! - `clock`: Enables reading the system time (`now`) that depends on the standard library for
|
//! - `clock`: Enables reading the system time (`now`) that depends on the standard library for
|
||||||
//! UNIX-like operating systems and the Windows API (`winapi`) for Windows.
|
//! UNIX-like operating systems and the Windows API (`winapi`) for Windows.
|
||||||
|
//! - `wasmbind`: Interface with the JS Date API for the `wasm32` target.
|
||||||
//!
|
//!
|
||||||
//! Optional features:
|
//! Optional features:
|
||||||
//!
|
//!
|
||||||
//! - [`serde`][]: Enable serialization/deserialization via serde.
|
//! - [`serde`][]: Enable serialization/deserialization via serde.
|
||||||
|
//! - `rkyv`: Enable serialization/deserialization via rkyv.
|
||||||
|
//! - `arbitrary`: construct arbitrary instances of a type with the Arbitrary crate.
|
||||||
//! - `unstable-locales`: Enable localization. This adds various methods with a
|
//! - `unstable-locales`: Enable localization. This adds various methods with a
|
||||||
//! `_localized` suffix. The implementation and API may change or even be
|
//! `_localized` suffix. The implementation and API may change or even be
|
||||||
//! removed in a patch release. Feedback welcome.
|
//! removed in a patch release. Feedback welcome.
|
||||||
@ -284,16 +288,12 @@
|
|||||||
//! Ok(fixed_dt.clone()));
|
//! Ok(fixed_dt.clone()));
|
||||||
//! assert_eq!(DateTime::<FixedOffset>::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
//! assert_eq!(DateTime::<FixedOffset>::parse_from_rfc3339("2014-11-28T21:00:09+09:00"), Ok(fixed_dt.clone()));
|
||||||
//!
|
//!
|
||||||
//! // method 3
|
|
||||||
//! assert_eq!(Utc.datetime_from_str("2014-11-28 12:00:09", "%Y-%m-%d %H:%M:%S"), Ok(dt.clone()));
|
|
||||||
//! assert_eq!(Utc.datetime_from_str("Fri Nov 28 12:00:09 2014", "%a %b %e %T %Y"), Ok(dt.clone()));
|
|
||||||
//!
|
|
||||||
//! // oops, the year is missing!
|
//! // oops, the year is missing!
|
||||||
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
//! assert!(DateTime::<FixedOffset>::parse_from_str("Fri Nov 28 12:00:09", "%a %b %e %T %Y").is_err());
|
||||||
//! // oops, the format string does not include the year at all!
|
//! // oops, the format string does not include the year at all!
|
||||||
//! assert!(Utc.datetime_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
|
//! assert!(DateTime::<FixedOffset>::parse_from_str("Fri Nov 28 12:00:09", "%a %b %e %T").is_err());
|
||||||
//! // oops, the weekday is incorrect!
|
//! // oops, the weekday is incorrect!
|
||||||
//! assert!(Utc.datetime_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
//! assert!(DateTime::<FixedOffset>::parse_from_str("Sat Nov 28 12:00:09 2014", "%a %b %e %T %Y").is_err());
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! Again : See [`format::strftime`](./format/strftime/index.html#specifiers)
|
//! Again : See [`format::strftime`](./format/strftime/index.html#specifiers)
|
||||||
@ -343,18 +343,18 @@
|
|||||||
//!
|
//!
|
||||||
//! ## Limitations
|
//! ## Limitations
|
||||||
//!
|
//!
|
||||||
//! Only proleptic Gregorian calendar (i.e. extended to support older dates) is supported.
|
//! Only the 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.
|
//! 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.
|
//! ## Rust version requirements
|
||||||
//! Time types are limited in the nanosecond accuracy.
|
|
||||||
//!
|
//!
|
||||||
//! [Leap seconds are supported in the representation but
|
//! The Minimum Supported Rust Version (MSRV) is currently **Rust 1.57.0**.
|
||||||
//! Chrono doesn't try to make use of them](./naive/struct.NaiveTime.html#leap-second-handling).
|
//!
|
||||||
//! (The main reason is that leap seconds are not really predictable.)
|
//! The MSRV is explicitly tested in CI. It may be bumped in minor releases, but this is not done
|
||||||
//! Almost *every* operation over the possible leap seconds will ignore them.
|
//! lightly.
|
||||||
//! Consider using `NaiveDateTime` with the implicit TAI (International Atomic Time) scale
|
|
||||||
//! if you want.
|
|
||||||
//!
|
//!
|
||||||
//! Chrono inherently does not support an inaccurate or partial date and time representation.
|
//! 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.
|
//! Any operation that can be ambiguous will return `None` in such cases.
|
||||||
@ -367,6 +367,89 @@
|
|||||||
//!
|
//!
|
||||||
//! Advanced time zone handling is not yet supported.
|
//! Advanced time zone handling is not yet supported.
|
||||||
//! For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
|
//! For now you can try the [Chrono-tz](https://github.com/chronotope/chrono-tz/) crate instead.
|
||||||
|
//!
|
||||||
|
//! ## Relation between chrono and time 0.1
|
||||||
|
//!
|
||||||
|
//! Rust first had a `time` module added to `std` in its 0.7 release. It later moved to
|
||||||
|
//! `libextra`, and then to a `libtime` library shipped alongside the standard library. In 2014
|
||||||
|
//! work on chrono started in order to provide a full-featured date and time library in Rust.
|
||||||
|
//! Some improvements from chrono made it into the standard library; notably, `chrono::Duration`
|
||||||
|
//! was included as `std::time::Duration` ([rust#15934]) in 2014.
|
||||||
|
//!
|
||||||
|
//! In preparation of Rust 1.0 at the end of 2014 `libtime` was moved out of the Rust distro and
|
||||||
|
//! into the `time` crate to eventually be redesigned ([rust#18832], [rust#18858]), like the
|
||||||
|
//! `num` and `rand` crates. Of course chrono kept its dependency on this `time` crate. `time`
|
||||||
|
//! started re-exporting `std::time::Duration` during this period. Later, the standard library was
|
||||||
|
//! changed to have a more limited unsigned `Duration` type ([rust#24920], [RFC 1040]), while the
|
||||||
|
//! `time` crate kept the full functionality with `time::Duration`. `time::Duration` had been a
|
||||||
|
//! part of chrono's public API.
|
||||||
|
//!
|
||||||
|
//! By 2016 `time` 0.1 lived under the `rust-lang-deprecated` organisation and was not actively
|
||||||
|
//! maintained ([time#136]). chrono absorbed the platform functionality and `Duration` type of the
|
||||||
|
//! `time` crate in [chrono#478] (the work started in [chrono#286]). In order to preserve
|
||||||
|
//! compatibility with downstream crates depending on `time` and `chrono` sharing a `Duration`
|
||||||
|
//! type, chrono kept depending on time 0.1. chrono offered the option to opt out of the `time`
|
||||||
|
//! dependency by disabling the `oldtime` feature (swapping it out for an effectively similar
|
||||||
|
//! chrono type). In 2019, @jhpratt took over maintenance on the `time` crate and released what
|
||||||
|
//! amounts to a new crate as `time` 0.2.
|
||||||
|
//!
|
||||||
|
//! [rust#15934]: https://github.com/rust-lang/rust/pull/15934
|
||||||
|
//! [rust#18832]: https://github.com/rust-lang/rust/pull/18832#issuecomment-62448221
|
||||||
|
//! [rust#18858]: https://github.com/rust-lang/rust/pull/18858
|
||||||
|
//! [rust#24920]: https://github.com/rust-lang/rust/pull/24920
|
||||||
|
//! [RFC 1040]: https://rust-lang.github.io/rfcs/1040-duration-reform.html
|
||||||
|
//! [time#136]: https://github.com/time-rs/time/issues/136
|
||||||
|
//! [chrono#286]: https://github.com/chronotope/chrono/pull/286
|
||||||
|
//! [chrono#478]: https://github.com/chronotope/chrono/pull/478
|
||||||
|
//!
|
||||||
|
//! ## Security advisories
|
||||||
|
//!
|
||||||
|
//! In November of 2020 [CVE-2020-26235] and [RUSTSEC-2020-0071] were opened against the `time` crate.
|
||||||
|
//! @quininer had found that calls to `localtime_r` may be unsound ([chrono#499]). Eventually, almost
|
||||||
|
//! a year later, this was also made into a security advisory against chrono as [RUSTSEC-2020-0159],
|
||||||
|
//! which had platform code similar to `time`.
|
||||||
|
//!
|
||||||
|
//! On Unix-like systems a process is given a timezone id or description via the `TZ` environment
|
||||||
|
//! variable. We need this timezone data to calculate the current local time from a value that is
|
||||||
|
//! in UTC, such as the time from the system clock. `time` 0.1 and chrono used the POSIX function
|
||||||
|
//! `localtime_r` to do the conversion to local time, which reads the `TZ` variable.
|
||||||
|
//!
|
||||||
|
//! Rust assumes the environment to be writable and uses locks to access it from multiple threads.
|
||||||
|
//! Some other programming languages and libraries use similar locking strategies, but these are
|
||||||
|
//! typically not shared across languages. More importantly, POSIX declares modifying the
|
||||||
|
//! environment in a multi-threaded process as unsafe, and `getenv` in libc can't be changed to
|
||||||
|
//! take a lock because it returns a pointer to the data (see [rust#27970] for more discussion).
|
||||||
|
//!
|
||||||
|
//! Since version 4.20 chrono no longer uses `localtime_r`, instead using Rust code to query the
|
||||||
|
//! timezone (from the `TZ` variable or via `iana-time-zone` as a fallback) and work with data
|
||||||
|
//! from the system timezone database directly. The code for this was forked from the [tz-rs crate]
|
||||||
|
//! by @x-hgg-x. As such, chrono now respects the Rust lock when reading the `TZ` environment
|
||||||
|
//! variable. In general, code should avoid modifying the environment.
|
||||||
|
//!
|
||||||
|
//! [CVE-2020-26235]: https://nvd.nist.gov/vuln/detail/CVE-2020-26235
|
||||||
|
//! [RUSTSEC-2020-0071]: https://rustsec.org/advisories/RUSTSEC-2020-0071
|
||||||
|
//! [chrono#499]: https://github.com/chronotope/chrono/pull/499
|
||||||
|
//! [RUSTSEC-2020-0159]: https://rustsec.org/advisories/RUSTSEC-2020-0159.html
|
||||||
|
//! [rust#27970]: https://github.com/rust-lang/rust/issues/27970
|
||||||
|
//! [chrono#677]: https://github.com/chronotope/chrono/pull/677
|
||||||
|
//! [tz-rs crate]: https://crates.io/crates/tz-rs
|
||||||
|
//!
|
||||||
|
//! ## Removing time 0.1
|
||||||
|
//!
|
||||||
|
//! Because time 0.1 has been unmaintained for years, however, the security advisory mentioned
|
||||||
|
//! above has not been addressed. While chrono maintainers were careful not to break backwards
|
||||||
|
//! compatibility with the `time::Duration` type, there has been a long stream of issues from
|
||||||
|
//! users inquiring about the time 0.1 dependency with the vulnerability. We investigated the
|
||||||
|
//! potential breakage of removing the time 0.1 dependency in [chrono#1095] using a crater-like
|
||||||
|
//! experiment and determined that the potential for breaking (public) dependencies is very low.
|
||||||
|
//! We reached out to those few crates that did still depend on compatibility with time 0.1.
|
||||||
|
//!
|
||||||
|
//! As such, for chrono 0.4.30 we have decided to swap out the time 0.1 `Duration` implementation
|
||||||
|
//! for a local one that will offer a strict superset of the existing API going forward. This
|
||||||
|
//! will prevent most downstream users from being affected by the security vulnerability in time
|
||||||
|
//! 0.1 while minimizing the ecosystem impact of semver-incompatible version churn.
|
||||||
|
//!
|
||||||
|
//! [chrono#1095]: https://github.com/chronotope/chrono/pull/1095
|
||||||
|
|
||||||
#![doc(html_root_url = "https://docs.rs/chrono/latest/", test(attr(deny(warnings))))]
|
#![doc(html_root_url = "https://docs.rs/chrono/latest/", test(attr(deny(warnings))))]
|
||||||
#![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507
|
#![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507
|
||||||
@ -385,14 +468,6 @@ use core::fmt;
|
|||||||
mod time_delta;
|
mod time_delta;
|
||||||
pub use time_delta::TimeDelta;
|
pub use time_delta::TimeDelta;
|
||||||
|
|
||||||
#[cfg(feature = "__doctest")]
|
|
||||||
#[cfg_attr(feature = "__doctest", cfg(doctest))]
|
|
||||||
use doc_comment::doctest;
|
|
||||||
|
|
||||||
#[cfg(feature = "__doctest")]
|
|
||||||
#[cfg_attr(feature = "__doctest", cfg(doctest))]
|
|
||||||
doctest!("../README.md");
|
|
||||||
|
|
||||||
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
|
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
@ -1409,6 +1409,21 @@ impl NaiveDate {
|
|||||||
NaiveWeek { date: *self, start }
|
NaiveWeek { date: *self, start }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this is a leap year.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # use chrono::NaiveDate;
|
||||||
|
/// assert_eq!(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap().leap_year(), true);
|
||||||
|
/// assert_eq!(NaiveDate::from_ymd_opt(2001, 1, 1).unwrap().leap_year(), false);
|
||||||
|
/// assert_eq!(NaiveDate::from_ymd_opt(2002, 1, 1).unwrap().leap_year(), false);
|
||||||
|
/// assert_eq!(NaiveDate::from_ymd_opt(2003, 1, 1).unwrap().leap_year(), false);
|
||||||
|
/// assert_eq!(NaiveDate::from_ymd_opt(2004, 1, 1).unwrap().leap_year(), true);
|
||||||
|
/// assert_eq!(NaiveDate::from_ymd_opt(2100, 1, 1).unwrap().leap_year(), false);
|
||||||
|
/// ```
|
||||||
|
pub const fn leap_year(&self) -> bool {
|
||||||
|
self.ymdf & (0b1000) == 0
|
||||||
|
}
|
||||||
|
|
||||||
// This duplicates `Datelike::year()`, because trait methods can't be const yet.
|
// This duplicates `Datelike::year()`, because trait methods can't be const yet.
|
||||||
#[inline]
|
#[inline]
|
||||||
const fn year(&self) -> i32 {
|
const fn year(&self) -> i32 {
|
||||||
@ -3172,6 +3187,16 @@ mod tests {
|
|||||||
assert!(dt.with_ordinal0(4294967295).is_none());
|
assert!(dt.with_ordinal0(4294967295).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_leap_year() {
|
||||||
|
for year in 0..=MAX_YEAR {
|
||||||
|
let date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
|
||||||
|
let is_leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||||
|
assert_eq!(date.leap_year(), is_leap);
|
||||||
|
assert_eq!(date.leap_year(), date.with_ordinal(366).is_some());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MAX_YEAR-12-31 minus 0000-01-01
|
// MAX_YEAR-12-31 minus 0000-01-01
|
||||||
// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + (0001-01-01 minus 0000-01-01) - 1 day
|
// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + (0001-01-01 minus 0000-01-01) - 1 day
|
||||||
// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + 365 days
|
// = ((MAX_YEAR+1)-01-01 minus 0001-01-01) + 365 days
|
||||||
|
@ -348,6 +348,51 @@ fn test_datetime_parse_from_str() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_datetime_parse_from_str_with_spaces() {
|
||||||
|
let parse_from_str = NaiveDateTime::parse_from_str;
|
||||||
|
let dt = NaiveDate::from_ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap();
|
||||||
|
// with varying spaces - should succeed
|
||||||
|
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S "), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35 ", " %b %d %Y %H:%M:%S "), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str(" Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str("\n\tAug 09 2013 23:54:35 ", "\n\t%b %d %Y %H:%M:%S "), Ok(dt),);
|
||||||
|
assert_eq!(parse_from_str("\tAug 09 2013 23:54:35\t", "\t%b %d %Y %H:%M:%S\t"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str("Aug 09 2013\t23:54:35", "%b %d %Y\t%H:%M:%S"), Ok(dt));
|
||||||
|
assert_eq!(parse_from_str("Aug 09 2013\t\t23:54:35", "%b %d %Y\t\t%H:%M:%S"), Ok(dt));
|
||||||
|
// with varying spaces - should fail
|
||||||
|
// leading whitespace in format
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35", " %b %d %Y %H:%M:%S").is_err());
|
||||||
|
// trailing whitespace in format
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err());
|
||||||
|
// extra mid-string whitespace in format
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
|
||||||
|
// mismatched leading whitespace
|
||||||
|
assert!(parse_from_str("\tAug 09 2013 23:54:35", "\n%b %d %Y %H:%M:%S").is_err());
|
||||||
|
// mismatched trailing whitespace
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S\n").is_err());
|
||||||
|
// mismatched mid-string whitespace
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y\t%H:%M:%S").is_err());
|
||||||
|
// trailing whitespace in format
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S ").is_err());
|
||||||
|
// trailing whitespace (newline) in format
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
|
||||||
|
// leading space in data
|
||||||
|
assert!(parse_from_str(" Aug 09 2013 23:54:35", "%b %d %Y %H:%M:%S").is_err());
|
||||||
|
// trailing space in data
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35 ", "%b %d %Y %H:%M:%S").is_err());
|
||||||
|
// trailing tab in data
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35\t", "%b %d %Y %H:%M:%S").is_err());
|
||||||
|
// mismatched newlines
|
||||||
|
assert!(parse_from_str("\nAug 09 2013 23:54:35", "%b %d %Y %H:%M:%S\n").is_err());
|
||||||
|
// trailing literal in data
|
||||||
|
assert!(parse_from_str("Aug 09 2013 23:54:35 !!!", "%b %d %Y %H:%M:%S ").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_add_sub_invariant() {
|
fn test_datetime_add_sub_invariant() {
|
||||||
// issue #37
|
// issue #37
|
||||||
|
@ -257,12 +257,4 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test Issue #866
|
|
||||||
#[test]
|
|
||||||
fn test_issue_866() {
|
|
||||||
#[allow(deprecated)]
|
|
||||||
let local_20221106 = Local.ymd(2022, 11, 6);
|
|
||||||
let _dt_20221106 = local_20221106.and_hms_milli_opt(1, 2, 59, 1000).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -859,8 +859,12 @@ mod tests {
|
|||||||
assert_eq!(time_zone_local, time_zone_local_1);
|
assert_eq!(time_zone_local, time_zone_local_1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_zone_utc = TimeZone::from_posix_tz("UTC")?;
|
// `TimeZone::from_posix_tz("UTC")` will return `Error` if the environment does not have
|
||||||
assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0);
|
// a time zone database, like for example some docker containers.
|
||||||
|
// In that case skip the test.
|
||||||
|
if let Ok(time_zone_utc) = TimeZone::from_posix_tz("UTC") {
|
||||||
|
assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(TimeZone::from_posix_tz("EST5EDT,0/0,J365/25").is_err());
|
assert!(TimeZone::from_posix_tz("EST5EDT,0/0,J365/25").is_err());
|
||||||
|
@ -444,6 +444,7 @@ pub trait TimeZone: Sized + Clone {
|
|||||||
///
|
///
|
||||||
/// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with
|
/// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with
|
||||||
/// parsed [`FixedOffset`].
|
/// parsed [`FixedOffset`].
|
||||||
|
#[deprecated(since = "0.4.29", note = "use `DateTime::parse_from_str` instead")]
|
||||||
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
fn datetime_from_str(&self, s: &str, fmt: &str) -> ParseResult<DateTime<Self>> {
|
||||||
let mut parsed = Parsed::new();
|
let mut parsed = Parsed::new();
|
||||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||||
|
@ -34,9 +34,9 @@ const SECS_PER_MINUTE: i64 = 60;
|
|||||||
/// The number of seconds in an hour.
|
/// The number of seconds in an hour.
|
||||||
const SECS_PER_HOUR: i64 = 3600;
|
const SECS_PER_HOUR: i64 = 3600;
|
||||||
/// The number of (non-leap) seconds in days.
|
/// The number of (non-leap) seconds in days.
|
||||||
const SECS_PER_DAY: i64 = 86400;
|
const SECS_PER_DAY: i64 = 86_400;
|
||||||
/// The number of (non-leap) seconds in a week.
|
/// The number of (non-leap) seconds in a week.
|
||||||
const SECS_PER_WEEK: i64 = 604800;
|
const SECS_PER_WEEK: i64 = 604_800;
|
||||||
|
|
||||||
macro_rules! try_opt {
|
macro_rules! try_opt {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
@ -492,19 +492,22 @@ mod tests {
|
|||||||
assert!(TimeDelta::seconds(1) != TimeDelta::zero());
|
assert!(TimeDelta::seconds(1) != TimeDelta::zero());
|
||||||
assert_eq!(TimeDelta::seconds(1) + TimeDelta::seconds(2), TimeDelta::seconds(3));
|
assert_eq!(TimeDelta::seconds(1) + TimeDelta::seconds(2), TimeDelta::seconds(3));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TimeDelta::seconds(86399) + TimeDelta::seconds(4),
|
TimeDelta::seconds(86_399) + TimeDelta::seconds(4),
|
||||||
TimeDelta::days(1) + TimeDelta::seconds(3)
|
TimeDelta::days(1) + TimeDelta::seconds(3)
|
||||||
);
|
);
|
||||||
assert_eq!(TimeDelta::days(10) - TimeDelta::seconds(1000), TimeDelta::seconds(863000));
|
assert_eq!(TimeDelta::days(10) - TimeDelta::seconds(1000), TimeDelta::seconds(863_000));
|
||||||
assert_eq!(TimeDelta::days(10) - TimeDelta::seconds(1000000), TimeDelta::seconds(-136000));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TimeDelta::days(2) + TimeDelta::seconds(86399) + TimeDelta::nanoseconds(1234567890),
|
TimeDelta::days(10) - TimeDelta::seconds(1_000_000),
|
||||||
TimeDelta::days(3) + TimeDelta::nanoseconds(234567890)
|
TimeDelta::seconds(-136_000)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
TimeDelta::days(2) + TimeDelta::seconds(86_399) + TimeDelta::nanoseconds(1_234_567_890),
|
||||||
|
TimeDelta::days(3) + TimeDelta::nanoseconds(234_567_890)
|
||||||
);
|
);
|
||||||
assert_eq!(-TimeDelta::days(3), TimeDelta::days(-3));
|
assert_eq!(-TimeDelta::days(3), TimeDelta::days(-3));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
-(TimeDelta::days(3) + TimeDelta::seconds(70)),
|
-(TimeDelta::days(3) + TimeDelta::seconds(70)),
|
||||||
TimeDelta::days(-4) + TimeDelta::seconds(86400 - 70)
|
TimeDelta::days(-4) + TimeDelta::seconds(86_400 - 70)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,10 +516,10 @@ mod tests {
|
|||||||
assert_eq!(TimeDelta::zero().num_days(), 0);
|
assert_eq!(TimeDelta::zero().num_days(), 0);
|
||||||
assert_eq!(TimeDelta::days(1).num_days(), 1);
|
assert_eq!(TimeDelta::days(1).num_days(), 1);
|
||||||
assert_eq!(TimeDelta::days(-1).num_days(), -1);
|
assert_eq!(TimeDelta::days(-1).num_days(), -1);
|
||||||
assert_eq!(TimeDelta::seconds(86399).num_days(), 0);
|
assert_eq!(TimeDelta::seconds(86_399).num_days(), 0);
|
||||||
assert_eq!(TimeDelta::seconds(86401).num_days(), 1);
|
assert_eq!(TimeDelta::seconds(86_401).num_days(), 1);
|
||||||
assert_eq!(TimeDelta::seconds(-86399).num_days(), 0);
|
assert_eq!(TimeDelta::seconds(-86_399).num_days(), 0);
|
||||||
assert_eq!(TimeDelta::seconds(-86401).num_days(), -1);
|
assert_eq!(TimeDelta::seconds(-86_401).num_days(), -1);
|
||||||
assert_eq!(TimeDelta::days(i32::MAX as i64).num_days(), i32::MAX as i64);
|
assert_eq!(TimeDelta::days(i32::MAX as i64).num_days(), i32::MAX as i64);
|
||||||
assert_eq!(TimeDelta::days(i32::MIN as i64).num_days(), i32::MIN as i64);
|
assert_eq!(TimeDelta::days(i32::MIN as i64).num_days(), i32::MIN as i64);
|
||||||
}
|
}
|
||||||
@ -705,7 +708,7 @@ mod tests {
|
|||||||
assert_eq!(TimeDelta::microseconds(42).to_string(), "PT0.000042S");
|
assert_eq!(TimeDelta::microseconds(42).to_string(), "PT0.000042S");
|
||||||
assert_eq!(TimeDelta::nanoseconds(42).to_string(), "PT0.000000042S");
|
assert_eq!(TimeDelta::nanoseconds(42).to_string(), "PT0.000000042S");
|
||||||
assert_eq!((TimeDelta::days(7) + TimeDelta::milliseconds(6543)).to_string(), "P7DT6.543S");
|
assert_eq!((TimeDelta::days(7) + TimeDelta::milliseconds(6543)).to_string(), "P7DT6.543S");
|
||||||
assert_eq!(TimeDelta::seconds(-86401).to_string(), "-P1DT1S");
|
assert_eq!(TimeDelta::seconds(-86_401).to_string(), "-P1DT1S");
|
||||||
assert_eq!(TimeDelta::nanoseconds(-1).to_string(), "-PT0.000000001S");
|
assert_eq!(TimeDelta::nanoseconds(-1).to_string(), "-PT0.000000001S");
|
||||||
|
|
||||||
// the format specifier should have no effect on `TimeDelta`
|
// the format specifier should have no effect on `TimeDelta`
|
||||||
@ -718,11 +721,14 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_to_std() {
|
fn test_to_std() {
|
||||||
assert_eq!(TimeDelta::seconds(1).to_std(), Ok(StdDuration::new(1, 0)));
|
assert_eq!(TimeDelta::seconds(1).to_std(), Ok(StdDuration::new(1, 0)));
|
||||||
assert_eq!(TimeDelta::seconds(86401).to_std(), Ok(StdDuration::new(86401, 0)));
|
assert_eq!(TimeDelta::seconds(86_401).to_std(), Ok(StdDuration::new(86_401, 0)));
|
||||||
assert_eq!(TimeDelta::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000)));
|
assert_eq!(TimeDelta::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123_000_000)));
|
||||||
assert_eq!(TimeDelta::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000)));
|
assert_eq!(
|
||||||
|
TimeDelta::milliseconds(123_765).to_std(),
|
||||||
|
Ok(StdDuration::new(123, 765_000_000))
|
||||||
|
);
|
||||||
assert_eq!(TimeDelta::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777)));
|
assert_eq!(TimeDelta::nanoseconds(777).to_std(), Ok(StdDuration::new(0, 777)));
|
||||||
assert_eq!(MAX.to_std(), Ok(StdDuration::new(9223372036854775, 807000000)));
|
assert_eq!(MAX.to_std(), Ok(StdDuration::new(9_223_372_036_854_775, 807_000_000)));
|
||||||
assert_eq!(TimeDelta::seconds(-1).to_std(), Err(OutOfRangeError(())));
|
assert_eq!(TimeDelta::seconds(-1).to_std(), Err(OutOfRangeError(())));
|
||||||
assert_eq!(TimeDelta::milliseconds(-1).to_std(), Err(OutOfRangeError(())));
|
assert_eq!(TimeDelta::milliseconds(-1).to_std(), Err(OutOfRangeError(())));
|
||||||
}
|
}
|
||||||
@ -730,23 +736,26 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_from_std() {
|
fn test_from_std() {
|
||||||
assert_eq!(Ok(TimeDelta::seconds(1)), TimeDelta::from_std(StdDuration::new(1, 0)));
|
assert_eq!(Ok(TimeDelta::seconds(1)), TimeDelta::from_std(StdDuration::new(1, 0)));
|
||||||
assert_eq!(Ok(TimeDelta::seconds(86401)), TimeDelta::from_std(StdDuration::new(86401, 0)));
|
assert_eq!(Ok(TimeDelta::seconds(86401)), TimeDelta::from_std(StdDuration::new(86_401, 0)));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(TimeDelta::milliseconds(123)),
|
Ok(TimeDelta::milliseconds(123)),
|
||||||
TimeDelta::from_std(StdDuration::new(0, 123000000))
|
TimeDelta::from_std(StdDuration::new(0, 123_000_000))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok(TimeDelta::milliseconds(123765)),
|
Ok(TimeDelta::milliseconds(123_765)),
|
||||||
TimeDelta::from_std(StdDuration::new(123, 765000000))
|
TimeDelta::from_std(StdDuration::new(123, 765_000_000))
|
||||||
);
|
);
|
||||||
assert_eq!(Ok(TimeDelta::nanoseconds(777)), TimeDelta::from_std(StdDuration::new(0, 777)));
|
assert_eq!(Ok(TimeDelta::nanoseconds(777)), TimeDelta::from_std(StdDuration::new(0, 777)));
|
||||||
assert_eq!(Ok(MAX), TimeDelta::from_std(StdDuration::new(9223372036854775, 807000000)));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TimeDelta::from_std(StdDuration::new(9223372036854776, 0)),
|
Ok(MAX),
|
||||||
|
TimeDelta::from_std(StdDuration::new(9_223_372_036_854_775, 807_000_000))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
TimeDelta::from_std(StdDuration::new(9_223_372_036_854_776, 0)),
|
||||||
Err(OutOfRangeError(()))
|
Err(OutOfRangeError(()))
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TimeDelta::from_std(StdDuration::new(9223372036854775, 807000001)),
|
TimeDelta::from_std(StdDuration::new(9_223_372_036_854_775, 807_000_001)),
|
||||||
Err(OutOfRangeError(()))
|
Err(OutOfRangeError(()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -316,6 +316,11 @@ pub trait Timelike: Sized {
|
|||||||
fn with_nanosecond(&self, nano: u32) -> Option<Self>;
|
fn with_nanosecond(&self, nano: u32) -> Option<Self>;
|
||||||
|
|
||||||
/// Returns the number of non-leap seconds past the last midnight.
|
/// Returns the number of non-leap seconds past the last midnight.
|
||||||
|
///
|
||||||
|
/// Every value in 00:00:00-23:59:59 maps to an integer in 0-86399.
|
||||||
|
///
|
||||||
|
/// This method is not intended to provide the real number of seconds since midnight on a given
|
||||||
|
/// day. It does not take things like DST transitions into account.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn num_seconds_from_midnight(&self) -> u32 {
|
fn num_seconds_from_midnight(&self) -> u32 {
|
||||||
self.hour() * 3600 + self.minute() * 60 + self.second()
|
self.hour() * 3600 + self.minute() * 60 + self.second()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user