mirror of
https://github.com/chronotope/chrono.git
synced 2025-09-30 06:21:56 +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
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
# nightly is required for --doctests, see cargo-llvm-cov#2
|
||||
- name: Install Rust (nightly)
|
||||
run: rustup update nightly
|
||||
- name: Install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@cargo-llvm-cov
|
||||
- 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
|
||||
uses: codecov/codecov-action@v3
|
||||
env:
|
||||
|
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@ -17,12 +17,16 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo fmt --check -- --color=always
|
||||
- run: cargo fmt --check --manifest-path fuzz/Cargo.toml
|
||||
- run: cargo fmt --check --manifest-path bench/Cargo.toml
|
||||
- run: |
|
||||
cargo clippy --all-features --all-targets --color=always \
|
||||
-- -D warnings
|
||||
- run: |
|
||||
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
|
||||
-- -D warnings
|
||||
- run: |
|
||||
cargo clippy --manifest-path bench/Cargo.toml --color=always \
|
||||
-- -D warnings
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
|
55
.github/workflows/test.yml
vendored
55
.github/workflows/test.yml
vendored
@ -6,22 +6,10 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
timezones_linux:
|
||||
timezones:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-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]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
tz: ["ACST-9:30", "EST4", "UTC0", "Asia/Katmandu"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@ -67,7 +55,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.rust_version }}
|
||||
- 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 --lib and --doc to avoid the long running integration tests
|
||||
# which are run elsewhere
|
||||
@ -119,7 +107,6 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
target:
|
||||
[
|
||||
wasm32-wasi,
|
||||
wasm32-unknown-emscripten,
|
||||
aarch64-apple-ios,
|
||||
aarch64-linux-android,
|
||||
@ -145,7 +132,6 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
@ -153,6 +139,27 @@ jobs:
|
||||
# with the host system.
|
||||
- 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:
|
||||
strategy:
|
||||
matrix:
|
||||
@ -165,6 +172,20 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- 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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -3,8 +3,8 @@ cff-version: 1.2.0
|
||||
message: Please cite this crate using these information.
|
||||
|
||||
# Version information.
|
||||
date-released: 2023-08-29
|
||||
version: 0.4.27
|
||||
date-released: 2023-09-07
|
||||
version: 0.4.30
|
||||
|
||||
# Project information.
|
||||
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"]
|
||||
wasmbind = ["wasm-bindgen", "js-sys"]
|
||||
unstable-locales = ["pure-rust-locales", "alloc"]
|
||||
__internal_bench = ["criterion"]
|
||||
__doctest = []
|
||||
__internal_bench = []
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.99", default-features = false, optional = true }
|
||||
pure-rust-locales = { version = "0.6", optional = true }
|
||||
criterion = { version = "0.4.0", optional = true }
|
||||
rkyv = { version = "0.7", 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_derive = { version = "1", default-features = false }
|
||||
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]
|
||||
wasm-bindgen-test = "0.3"
|
||||
@ -66,13 +63,3 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.playground]
|
||||
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
|
||||
#![cfg(feature = "__internal_bench")]
|
||||
|
||||
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 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`].
|
||||
///
|
||||
/// 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.
|
||||
/// See [RFC 2822 Appendix A.5]
|
||||
/// # Support for the obsolete date format
|
||||
///
|
||||
/// - 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};
|
||||
@ -672,8 +686,6 @@ impl DateTime<FixedOffset> {
|
||||
/// 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>> {
|
||||
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)];
|
||||
let mut parsed = Parsed::new();
|
||||
|
@ -311,6 +311,7 @@ fn ymdhms_nano(
|
||||
}
|
||||
|
||||
// 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> {
|
||||
Utc.with_ymd_and_hms(year, month, day, hour, min, sec).unwrap()
|
||||
}
|
||||
@ -818,6 +819,7 @@ fn test_datetime_from_str() {
|
||||
.unwrap())
|
||||
);
|
||||
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.
|
||||
}
|
||||
@ -915,7 +917,7 @@ fn test_parse_datetime_utc() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utc_datetime_from_str() {
|
||||
fn test_parse_from_str() {
|
||||
let edt = FixedOffset::east_opt(570 * 60).unwrap();
|
||||
let edt0 = FixedOffset::east_opt(0).unwrap();
|
||||
let wdt = FixedOffset::west_opt(10 * 3600).unwrap();
|
||||
@ -930,11 +932,7 @@ fn test_utc_datetime_from_str() {
|
||||
)
|
||||
.is_err());
|
||||
assert_eq!(
|
||||
Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"),
|
||||
Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
DateTime::<FixedOffset>::parse_from_str("0", "%s").unwrap(),
|
||||
DateTime::<Utc>::parse_from_str("0", "%s").unwrap(),
|
||||
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.
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn test_datetime_parse_from_str() {
|
||||
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());
|
||||
}
|
||||
|
||||
// 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]
|
||||
#[cfg(feature = "std")]
|
||||
#[cfg(all(feature = "std", not(all(target_arch = "wasm32", target_os = "wasi"))))]
|
||||
fn test_from_system_time() {
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
|
@ -18,14 +18,14 @@
|
||||
//! # Example
|
||||
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
|
||||
#![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 formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S"));
|
||||
//! 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);
|
||||
//! # 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`
|
||||
|
||||
/// 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
|
||||
///
|
||||
@ -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
|
||||
///
|
||||
|
@ -614,7 +614,7 @@ fn parse_rfc3339_relaxed<'a>(parsed: &mut Parsed, mut s: &'a str) -> ParseResult
|
||||
Ok(_) => return Err(NOT_ENOUGH),
|
||||
};
|
||||
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)
|
||||
} else {
|
||||
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)]
|
||||
mod tests {
|
||||
use crate::format::*;
|
||||
use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc};
|
||||
use crate::{DateTime, FixedOffset, NaiveDateTime, TimeZone, Timelike, Utc};
|
||||
|
||||
macro_rules! parsed {
|
||||
($($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");
|
||||
|
||||
// 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
|
||||
// Sunday parsed incorrectly).
|
||||
@ -1765,7 +1768,7 @@ mod tests {
|
||||
];
|
||||
|
||||
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 = [
|
||||
@ -1784,7 +1787,7 @@ mod tests {
|
||||
];
|
||||
|
||||
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]
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[cfg(all(feature = "unstable-locales", target_pointer_width = "64"))]
|
||||
fn test_type_sizes() {
|
||||
use core::mem::size_of;
|
||||
assert_eq!(size_of::<Item>(), 24);
|
||||
assert_eq!(size_of::<StrftimeItems>(), 56);
|
||||
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
|
||||
//!
|
||||
//! 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 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,
|
||||
//! which Chrono builds upon and should acknowledge:
|
||||
//!
|
||||
//! * [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)
|
||||
//! 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.
|
||||
//!
|
||||
//! ### Features
|
||||
//!
|
||||
@ -29,10 +30,13 @@
|
||||
//! and traits.
|
||||
//! - `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.
|
||||
//! - `wasmbind`: Interface with the JS Date API for the `wasm32` target.
|
||||
//!
|
||||
//! Optional features:
|
||||
//!
|
||||
//! - [`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
|
||||
//! `_localized` suffix. The implementation and API may change or even be
|
||||
//! removed in a patch release. Feedback welcome.
|
||||
@ -284,16 +288,12 @@
|
||||
//! 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!
|
||||
//! 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!
|
||||
//! 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!
|
||||
//! 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)
|
||||
@ -343,18 +343,18 @@
|
||||
//!
|
||||
//! ## 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.
|
||||
//! ## Rust version requirements
|
||||
//!
|
||||
//! [Leap seconds are supported in the representation but
|
||||
//! 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.)
|
||||
//! 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.
|
||||
//! The Minimum Supported Rust Version (MSRV) is currently **Rust 1.57.0**.
|
||||
//!
|
||||
//! The MSRV is explicitly tested in CI. It may be bumped in minor releases, but this is not done
|
||||
//! lightly.
|
||||
//!
|
||||
//! 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.
|
||||
@ -367,6 +367,89 @@
|
||||
//!
|
||||
//! Advanced time zone handling is not yet supported.
|
||||
//! 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))))]
|
||||
#![cfg_attr(feature = "bench", feature(test))] // lib stability features as per RFC #507
|
||||
@ -385,14 +468,6 @@ use core::fmt;
|
||||
mod time_delta;
|
||||
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::*;`).
|
||||
pub mod prelude {
|
||||
#[doc(no_inline)]
|
||||
|
@ -1409,6 +1409,21 @@ impl NaiveDate {
|
||||
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.
|
||||
#[inline]
|
||||
const fn year(&self) -> i32 {
|
||||
@ -3172,6 +3187,16 @@ mod tests {
|
||||
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+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
|
||||
|
@ -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]
|
||||
fn test_datetime_add_sub_invariant() {
|
||||
// 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);
|
||||
}
|
||||
|
||||
let time_zone_utc = TimeZone::from_posix_tz("UTC")?;
|
||||
assert_eq!(time_zone_utc.find_local_time_type(0)?.offset(), 0);
|
||||
// `TimeZone::from_posix_tz("UTC")` will return `Error` if the environment does not have
|
||||
// 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());
|
||||
|
@ -444,6 +444,7 @@ pub trait TimeZone: Sized + Clone {
|
||||
///
|
||||
/// See also [`DateTime::parse_from_str`] which gives a [`DateTime`] with
|
||||
/// 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>> {
|
||||
let mut parsed = Parsed::new();
|
||||
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
|
||||
|
@ -34,9 +34,9 @@ const SECS_PER_MINUTE: i64 = 60;
|
||||
/// The number of seconds in an hour.
|
||||
const SECS_PER_HOUR: i64 = 3600;
|
||||
/// 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.
|
||||
const SECS_PER_WEEK: i64 = 604800;
|
||||
const SECS_PER_WEEK: i64 = 604_800;
|
||||
|
||||
macro_rules! try_opt {
|
||||
($e:expr) => {
|
||||
@ -492,19 +492,22 @@ mod tests {
|
||||
assert!(TimeDelta::seconds(1) != TimeDelta::zero());
|
||||
assert_eq!(TimeDelta::seconds(1) + TimeDelta::seconds(2), TimeDelta::seconds(3));
|
||||
assert_eq!(
|
||||
TimeDelta::seconds(86399) + TimeDelta::seconds(4),
|
||||
TimeDelta::seconds(86_399) + TimeDelta::seconds(4),
|
||||
TimeDelta::days(1) + TimeDelta::seconds(3)
|
||||
);
|
||||
assert_eq!(TimeDelta::days(10) - TimeDelta::seconds(1000), TimeDelta::seconds(863000));
|
||||
assert_eq!(TimeDelta::days(10) - TimeDelta::seconds(1000000), TimeDelta::seconds(-136000));
|
||||
assert_eq!(TimeDelta::days(10) - TimeDelta::seconds(1000), TimeDelta::seconds(863_000));
|
||||
assert_eq!(
|
||||
TimeDelta::days(2) + TimeDelta::seconds(86399) + TimeDelta::nanoseconds(1234567890),
|
||||
TimeDelta::days(3) + TimeDelta::nanoseconds(234567890)
|
||||
TimeDelta::days(10) - TimeDelta::seconds(1_000_000),
|
||||
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::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::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(86401).num_days(), 1);
|
||||
assert_eq!(TimeDelta::seconds(-86399).num_days(), 0);
|
||||
assert_eq!(TimeDelta::seconds(-86401).num_days(), -1);
|
||||
assert_eq!(TimeDelta::seconds(86_399).num_days(), 0);
|
||||
assert_eq!(TimeDelta::seconds(86_401).num_days(), 1);
|
||||
assert_eq!(TimeDelta::seconds(-86_399).num_days(), 0);
|
||||
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::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::nanoseconds(42).to_string(), "PT0.000000042S");
|
||||
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");
|
||||
|
||||
// the format specifier should have no effect on `TimeDelta`
|
||||
@ -718,11 +721,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_to_std() {
|
||||
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::milliseconds(123).to_std(), Ok(StdDuration::new(0, 123000000)));
|
||||
assert_eq!(TimeDelta::milliseconds(123765).to_std(), Ok(StdDuration::new(123, 765000000)));
|
||||
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, 123_000_000)));
|
||||
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!(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::milliseconds(-1).to_std(), Err(OutOfRangeError(())));
|
||||
}
|
||||
@ -730,23 +736,26 @@ mod tests {
|
||||
#[test]
|
||||
fn test_from_std() {
|
||||
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!(
|
||||
Ok(TimeDelta::milliseconds(123)),
|
||||
TimeDelta::from_std(StdDuration::new(0, 123000000))
|
||||
TimeDelta::from_std(StdDuration::new(0, 123_000_000))
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(TimeDelta::milliseconds(123765)),
|
||||
TimeDelta::from_std(StdDuration::new(123, 765000000))
|
||||
Ok(TimeDelta::milliseconds(123_765)),
|
||||
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(MAX), TimeDelta::from_std(StdDuration::new(9223372036854775, 807000000)));
|
||||
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(()))
|
||||
);
|
||||
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(()))
|
||||
);
|
||||
}
|
||||
|
@ -316,6 +316,11 @@ pub trait Timelike: Sized {
|
||||
fn with_nanosecond(&self, nano: u32) -> Option<Self>;
|
||||
|
||||
/// 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]
|
||||
fn num_seconds_from_midnight(&self) -> u32 {
|
||||
self.hour() * 3600 + self.minute() * 60 + self.second()
|
||||
|
Loading…
x
Reference in New Issue
Block a user