mirror of
https://github.com/chronotope/chrono.git
synced 2025-09-28 21:42:01 +00:00
Merge pull request #1141 from pitdicker/merge_0.4.x
Merge 0.4.x into main
This commit is contained in:
commit
38b19bbe4e
11
.github/workflows/lint.yml
vendored
11
.github/workflows/lint.yml
vendored
@ -17,9 +17,8 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo fmt --check -- --color=always
|
||||
- run: cargo fmt --check --manifest-path fuzz/Cargo.toml
|
||||
- run: cargo clippy --color=always -- -D warnings
|
||||
- run: |
|
||||
cargo clippy --color=always --target x86_64-pc-windows-msvc \
|
||||
cargo clippy --all-features --all-targets --color=always \
|
||||
-- -D warnings
|
||||
- run: |
|
||||
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
|
||||
@ -27,6 +26,14 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
toml:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: tamasfe/taplo:0.8.0
|
||||
steps:
|
||||
- run: taplo lint
|
||||
- run: taplo fmt --check --diff
|
||||
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
23
.github/workflows/test.yml
vendored
23
.github/workflows/test.yml
vendored
@ -28,8 +28,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo test --lib --all-features --color=always -- --color=always
|
||||
- run: cargo test --doc --all-features --color=always -- --color=always
|
||||
- run: cargo test --all-features --color=always -- --color=always
|
||||
|
||||
# later this may be able to be included with the below
|
||||
# kept separate for now as the following don't compile on 1.56.1
|
||||
@ -118,7 +117,6 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
target:
|
||||
[
|
||||
wasm32-unknown-unknown,
|
||||
wasm32-wasi,
|
||||
wasm32-unknown-emscripten,
|
||||
aarch64-apple-ios,
|
||||
@ -131,19 +129,11 @@ jobs:
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "12"
|
||||
- run: |
|
||||
set -euxo pipefail
|
||||
export RUST_BACKTRACE=1
|
||||
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf \
|
||||
| bash --noprofile --norc
|
||||
wasm-pack --version
|
||||
shell: bash
|
||||
- run: cargo build --target ${{ matrix.target }} --color=always
|
||||
|
||||
features_check_wasm:
|
||||
test_wasm:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
@ -155,10 +145,11 @@ jobs:
|
||||
targets: wasm32-unknown-unknown
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: |
|
||||
cargo hack check --feature-powerset --optional-deps serde,rkyv \
|
||||
--skip default --skip __internal_bench --skip __doctest \
|
||||
--skip iana-time-zone --skip pure-rust-locales
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: jetli/wasm-pack-action@v0.4.0
|
||||
# The `TZ` and `NOW` variables are used to compare the results inside the WASM environment
|
||||
# with the host system.
|
||||
- run: TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind
|
||||
|
||||
cross-targets:
|
||||
strategy:
|
||||
|
@ -31,12 +31,12 @@ __doctest = []
|
||||
serde = { version = "1.0.99", default-features = false, optional = true }
|
||||
pure-rust-locales = { version = "0.5.2", 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 }
|
||||
|
||||
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies]
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
|
||||
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.48.0", features = ["Win32_System_Time", "Win32_System_SystemInformation", "Win32_Foundation"], optional = true }
|
||||
|
26
Makefile
26
Makefile
@ -1,26 +0,0 @@
|
||||
# this Makefile is mostly for the packaging convenience.
|
||||
# casual users should use `cargo` to retrieve the appropriate version of Chrono.
|
||||
|
||||
CHANNEL=stable
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
@echo 'Try `cargo build` instead.'
|
||||
|
||||
.PHONY: authors
|
||||
authors:
|
||||
echo 'Chrono is mainly written by Kang Seonghoon <public+rust@mearie.org>,' > AUTHORS.txt
|
||||
echo 'and also the following people (in ascending order):' >> AUTHORS.txt
|
||||
echo >> AUTHORS.txt
|
||||
git log --format='%aN <%aE>' | grep -v 'Kang Seonghoon' | sort -u >> AUTHORS.txt
|
||||
|
||||
.PHONY: readme README.md
|
||||
readme: README.md
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
CHANNEL=$(CHANNEL) ./ci/travis.sh
|
||||
|
||||
.PHONY: doc
|
||||
doc: authors readme
|
||||
cargo doc --features 'serde bincode'
|
@ -2,8 +2,8 @@
|
||||
name = "core-test"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Kang Seonghoon <public+rust@mearie.org>",
|
||||
"Brandon W Maister <quodlibetor@gmail.com>",
|
||||
"Kang Seonghoon <public+rust@mearie.org>",
|
||||
"Brandon W Maister <quodlibetor@gmail.com>",
|
||||
]
|
||||
edition = "2018"
|
||||
|
||||
@ -11,4 +11,4 @@ edition = "2018"
|
||||
chrono = { path = "../..", default-features = false, features = ["serde"] }
|
||||
|
||||
[features]
|
||||
alloc = ["chrono/alloc"]
|
||||
alloc = ["chrono/alloc"]
|
||||
|
@ -4,8 +4,8 @@ copyleft = "deny"
|
||||
|
||||
[advisories]
|
||||
ignore = [
|
||||
"RUSTSEC-2021-0145", # atty (dev-deps only, dependency of criterion)
|
||||
"RUSTSEC-2022-0004", # rustc_serialize, cannot remove due to compatibility
|
||||
"RUSTSEC-2021-0145", # atty (dev-deps only, dependency of criterion)
|
||||
"RUSTSEC-2022-0004", # rustc_serialize, cannot remove due to compatibility
|
||||
]
|
||||
unmaintained = "deny"
|
||||
unsound = "deny"
|
||||
|
@ -74,8 +74,6 @@ pub const MAX_DATE: Date<Utc> = Date::<Utc>::MAX_UTC;
|
||||
impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Makes a new `Date` with given *UTC* date and offset.
|
||||
/// The local date should be constructed via the `TimeZone` trait.
|
||||
//
|
||||
// note: this constructor is purposely not named to `new` to discourage the direct usage.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
|
||||
@ -85,7 +83,7 @@ impl<Tz: TimeZone> Date<Tz> {
|
||||
/// Makes a new `DateTime` from the current date and given `NaiveTime`.
|
||||
/// The offset in the current date is preserved.
|
||||
///
|
||||
/// Panics on invalid datetime.
|
||||
/// Returns `None` on invalid datetime.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
|
||||
|
@ -8,7 +8,6 @@ extern crate alloc;
|
||||
|
||||
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||
use alloc::string::{String, ToString};
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::cmp::Ordering;
|
||||
use core::fmt::Write;
|
||||
@ -102,8 +101,6 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp_opt(61, 0).unwrap(), Utc);
|
||||
/// assert_eq!(Utc.timestamp_opt(61, 0).unwrap(), dt);
|
||||
/// ```
|
||||
//
|
||||
// note: this constructor is purposely not named to `new` to discourage the direct usage.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
|
||||
@ -113,6 +110,13 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
/// Makes a new `DateTime` with given **local** datetime and offset that
|
||||
/// presents local timezone.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the local datetime can't be converted to UTC because it would be out of range.
|
||||
///
|
||||
/// This can happen if `datetime` is near the end of the representable range of `NaiveDateTime`,
|
||||
/// and the offset from UTC pushes it beyond that.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -142,11 +146,19 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
DateTime { datetime: datetime_utc, offset }
|
||||
}
|
||||
|
||||
/// Retrieves a date component
|
||||
/// Retrieves the date component with an associated timezone.
|
||||
///
|
||||
/// Unless you are immediately planning on turning this into a `DateTime`
|
||||
/// with the same Timezone you should use the
|
||||
/// [`date_naive`](DateTime::date_naive) method.
|
||||
/// with the same timezone you should use the [`date_naive`](DateTime::date_naive) method.
|
||||
///
|
||||
/// [`NaiveDate`] is a more well-defined type, and has more traits implemented on it,
|
||||
/// so should be preferred to [`Date`] any time you truly want to operate on dates.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This
|
||||
/// method will panic if the offset from UTC would push the local date outside of the
|
||||
/// representable range of a [`Date`].
|
||||
#[inline]
|
||||
#[deprecated(since = "0.4.23", note = "Use `date_naive()` instead")]
|
||||
#[allow(deprecated)]
|
||||
@ -155,10 +167,15 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
Date::from_utc(self.naive_local().date(), self.offset.clone())
|
||||
}
|
||||
|
||||
/// Retrieves the Date without an associated timezone
|
||||
/// Retrieves the date component.
|
||||
///
|
||||
/// [`NaiveDate`] is a more well-defined type, and has more traits implemented on it,
|
||||
/// so should be preferred to [`Date`] any time you truly want to operate on Dates.
|
||||
/// # Panics
|
||||
///
|
||||
/// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This
|
||||
/// method will panic if the offset from UTC would push the local date outside of the
|
||||
/// representable range of a [`NaiveDate`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::prelude::*;
|
||||
@ -174,8 +191,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
NaiveDate::from_ymd_opt(local.year(), local.month(), local.day()).unwrap()
|
||||
}
|
||||
|
||||
/// Retrieves a time component.
|
||||
/// Unlike `date`, this is not associated to the time zone.
|
||||
/// Retrieves the time component.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn time(&self) -> NaiveTime {
|
||||
@ -190,12 +206,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
self.datetime.timestamp()
|
||||
}
|
||||
|
||||
/// Returns the number of non-leap-milliseconds since January 1, 1970 UTC
|
||||
///
|
||||
/// Note that this does reduce the number of years that can be represented
|
||||
/// from ~584 Billion to ~584 Million. (If this is a problem, please file
|
||||
/// an issue to let me know what domain needs millisecond precision over
|
||||
/// billions of years, I'm curious.)
|
||||
/// Returns the number of non-leap-milliseconds since January 1, 1970 UTC.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -214,12 +225,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
self.datetime.timestamp_millis()
|
||||
}
|
||||
|
||||
/// Returns the number of non-leap-microseconds since January 1, 1970 UTC
|
||||
///
|
||||
/// Note that this does reduce the number of years that can be represented
|
||||
/// from ~584 Billion to ~584 Thousand. (If this is a problem, please file
|
||||
/// an issue to let me know what domain needs microsecond precision over
|
||||
/// millennia, I'm curious.)
|
||||
/// Returns the number of non-leap-microseconds since January 1, 1970 UTC.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -238,12 +244,15 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
self.datetime.timestamp_micros()
|
||||
}
|
||||
|
||||
/// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC
|
||||
/// Returns the number of non-leap-nanoseconds since January 1, 1970 UTC.
|
||||
///
|
||||
/// Note that this does reduce the number of years that can be represented
|
||||
/// from ~584 Billion to ~584. (If this is a problem, please file
|
||||
/// an issue to let me know what domain needs nanosecond precision over
|
||||
/// millennia, I'm curious.)
|
||||
/// # Panics
|
||||
///
|
||||
/// An `i64` with nanosecond precision can span a range of ~584 years. This function panics on
|
||||
/// an out of range `DateTime`.
|
||||
///
|
||||
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
|
||||
/// 2262-04-11T23:47:16.854775804.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -262,22 +271,18 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
self.datetime.timestamp_nanos()
|
||||
}
|
||||
|
||||
/// Returns the number of milliseconds since the last second boundary
|
||||
/// Returns the number of milliseconds since the last second boundary.
|
||||
///
|
||||
/// warning: in event of a leap second, this may exceed 999
|
||||
///
|
||||
/// note: this is not the number of milliseconds since January 1, 1970 0:00:00 UTC
|
||||
/// In event of a leap second this may exceed 999.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn timestamp_subsec_millis(&self) -> u32 {
|
||||
self.datetime.timestamp_subsec_millis()
|
||||
}
|
||||
|
||||
/// Returns the number of microseconds since the last second boundary
|
||||
/// Returns the number of microseconds since the last second boundary.
|
||||
///
|
||||
/// warning: in event of a leap second, this may exceed 999_999
|
||||
///
|
||||
/// note: this is not the number of microseconds since January 1, 1970 0:00:00 UTC
|
||||
/// In event of a leap second this may exceed 999,999.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn timestamp_subsec_micros(&self) -> u32 {
|
||||
@ -286,9 +291,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
|
||||
/// Returns the number of nanoseconds since the last second boundary
|
||||
///
|
||||
/// warning: in event of a leap second, this may exceed 999_999_999
|
||||
///
|
||||
/// note: this is not the number of nanoseconds since January 1, 1970 0:00:00 UTC
|
||||
/// In event of a leap second this may exceed 999,999,999.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn timestamp_subsec_nanos(&self) -> u32 {
|
||||
@ -310,7 +313,8 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
}
|
||||
|
||||
/// Changes the associated time zone.
|
||||
/// The returned `DateTime` references the same instant of time from the perspective of the provided time zone.
|
||||
/// The returned `DateTime` references the same instant of time from the perspective of the
|
||||
/// provided time zone.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> {
|
||||
@ -327,7 +331,9 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
|
||||
/// Adds given `Duration` to the current date and time.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<DateTime<Tz>> {
|
||||
@ -338,10 +344,16 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
|
||||
/// Adds given `Months` to the current date and time.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow, or if the
|
||||
/// local time is not valid on the newly calculated date.
|
||||
/// Uses the last day of the month if the day does not exist in the resulting month.
|
||||
///
|
||||
/// See [`NaiveDate::checked_add_months`] for more details on behavior
|
||||
/// See [`NaiveDate::checked_add_months`] for more details on behavior.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date would be out of range.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[must_use]
|
||||
pub fn checked_add_months(self, rhs: Months) -> Option<DateTime<Tz>> {
|
||||
self.naive_local()
|
||||
@ -352,7 +364,9 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
|
||||
/// Subtracts given `Duration` from the current date and time.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<DateTime<Tz>> {
|
||||
@ -363,10 +377,16 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
|
||||
/// Subtracts given `Months` from the current date and time.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow, or if the
|
||||
/// local time is not valid on the newly calculated date.
|
||||
/// Uses the last day of the month if the day does not exist in the resulting month.
|
||||
///
|
||||
/// See [`NaiveDate::checked_sub_months`] for more details on behavior
|
||||
/// See [`NaiveDate::checked_sub_months`] for more details on behavior.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date would be out of range.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[must_use]
|
||||
pub fn checked_sub_months(self, rhs: Months) -> Option<DateTime<Tz>> {
|
||||
self.naive_local()
|
||||
@ -375,9 +395,14 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
.single()
|
||||
}
|
||||
|
||||
/// Add a duration in [`Days`] to the date part of the `DateTime`
|
||||
/// Add a duration in [`Days`] to the date part of the `DateTime`.
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date would be out of range.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[must_use]
|
||||
pub fn checked_add_days(self, days: Days) -> Option<Self> {
|
||||
self.naive_local()
|
||||
@ -386,9 +411,14 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
.single()
|
||||
}
|
||||
|
||||
/// Subtract a duration in [`Days`] from the date part of the `DateTime`
|
||||
/// Subtract a duration in [`Days`] from the date part of the `DateTime`.
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date would be out of range.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[must_use]
|
||||
pub fn checked_sub_days(self, days: Days) -> Option<Self> {
|
||||
self.naive_local()
|
||||
@ -401,8 +431,11 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
/// This does not overflow or underflow at all.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: DateTime<Tz2>) -> TimeDelta {
|
||||
self.datetime.signed_duration_since(rhs.datetime)
|
||||
pub fn signed_duration_since<Tz2: TimeZone>(
|
||||
self,
|
||||
rhs: impl Borrow<DateTime<Tz2>>,
|
||||
) -> TimeDelta {
|
||||
self.datetime.signed_duration_since(rhs.borrow().datetime)
|
||||
}
|
||||
|
||||
/// Returns a view to the naive UTC datetime.
|
||||
@ -413,6 +446,12 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
}
|
||||
|
||||
/// Returns a view to the naive local datetime.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// [`DateTime`] internally stores the date and time in UTC with a [`NaiveDateTime`]. This
|
||||
/// method will panic if the offset from UTC would push the local datetime outside of the
|
||||
/// representable range of a [`NaiveDateTime`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn naive_local(&self) -> NaiveDateTime {
|
||||
@ -420,6 +459,10 @@ impl<Tz: TimeZone> DateTime<Tz> {
|
||||
}
|
||||
|
||||
/// Retrieve the elapsed years from now to the given [`DateTime`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `base < self`.
|
||||
#[must_use]
|
||||
pub fn years_since(&self, base: Self) -> Option<u32> {
|
||||
let mut years = self.year() - base.year();
|
||||
@ -715,6 +758,11 @@ where
|
||||
Tz::Offset: fmt::Display,
|
||||
{
|
||||
/// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the date can not be represented in this format: the year may not be negative and
|
||||
/// can not have more than 4 digits.
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
|
||||
#[must_use]
|
||||
@ -917,35 +965,112 @@ impl<Tz: TimeZone> Datelike for DateTime<Tz> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Makes a new `DateTime` with the year number changed, while keeping the same month and day.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_year`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - When the `NaiveDateTime` would be out of range.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
fn with_year(&self, year: i32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_year(year))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the month number (starting from 1) changed.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_month`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - The value for `month` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_month(&self, month: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_month(month))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_month0`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - The value for `month0` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_month0(&self, month0: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_month0(month0))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_day`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - The value for `day` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_day(&self, day: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_day(day))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_day0`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - The value for `day0` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_day0(&self, day0: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_day0(day0))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_ordinal`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - The value for `ordinal` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_ordinal(&self, ordinal: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_ordinal(ordinal))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_ordinal0`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The resulting date does not exist.
|
||||
/// - The value for `ordinal0` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_ordinal0(&self, ordinal0: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_ordinal0(ordinal0))
|
||||
@ -970,21 +1095,64 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
|
||||
self.naive_local().nanosecond()
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the hour number changed.
|
||||
///
|
||||
/// See also the [`NaiveTime::with_hour`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The value for `hour` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_hour(&self, hour: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_hour(hour))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the minute number changed.
|
||||
///
|
||||
/// See also the [`NaiveTime::with_minute`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// - The value for `minute` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_minute(&self, min: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_minute(min))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with the second number changed.
|
||||
///
|
||||
/// As with the [`second`](#method.second) method,
|
||||
/// the input range is restricted to 0 through 59.
|
||||
///
|
||||
/// See also the [`NaiveTime::with_second`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The value for `second` is invalid.
|
||||
/// - The local time at the resulting date does not exist or is ambiguous, for example during a
|
||||
/// daylight saving time transition.
|
||||
#[inline]
|
||||
fn with_second(&self, sec: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_second(sec))
|
||||
}
|
||||
|
||||
/// Makes a new `DateTime` with nanoseconds since the whole non-leap second changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
/// As with the [`NaiveDateTime::nanosecond`] method,
|
||||
/// the input range can exceed 1,000,000,000 for leap seconds.
|
||||
///
|
||||
/// See also the [`NaiveTime::with_nanosecond`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `nanosecond >= 2,000,000,000`.
|
||||
#[inline]
|
||||
fn with_nanosecond(&self, nano: u32) -> Option<DateTime<Tz>> {
|
||||
map_local(self, |datetime| datetime.with_nanosecond(nano))
|
||||
@ -1099,6 +1267,15 @@ impl<Tz: TimeZone> Sub<DateTime<Tz>> for DateTime<Tz> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Sub<&DateTime<Tz>> for DateTime<Tz> {
|
||||
type Output = TimeDelta;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: &DateTime<Tz>) -> TimeDelta {
|
||||
self.signed_duration_since(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Tz: TimeZone> Add<Days> for DateTime<Tz> {
|
||||
type Output = DateTime<Tz>;
|
||||
|
||||
@ -1299,34 +1476,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sub_months() {
|
||||
let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap());
|
||||
|
||||
let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
|
||||
assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap());
|
||||
|
||||
let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap());
|
||||
|
||||
let utc_dt = Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
|
||||
assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_conversion() {
|
||||
let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
|
||||
let cdt_dt = FixedOffset::west_opt(5 * 60 * 60)
|
||||
.unwrap()
|
||||
.with_ymd_and_hms(2018, 9, 5, 18, 58, 0)
|
||||
.unwrap();
|
||||
let utc_dt2: DateTime<Utc> = cdt_dt.into();
|
||||
assert_eq!(utc_dt, utc_dt2);
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
fn test_encodable_json<FUtc, FFixed, E>(to_string_utc: FUtc, to_string_fixed: FFixed)
|
||||
where
|
||||
|
@ -158,7 +158,6 @@ pub mod ts_nanoseconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -186,7 +185,6 @@ pub mod ts_nanoseconds {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355733).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -285,7 +283,6 @@ pub mod ts_nanoseconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -316,7 +313,6 @@ pub mod ts_nanoseconds_option {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355733).single() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -417,7 +413,6 @@ pub mod ts_microseconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -445,7 +440,6 @@ pub mod ts_microseconds {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355000).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -543,7 +537,6 @@ pub mod ts_microseconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -574,7 +567,6 @@ pub mod ts_microseconds_option {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918355000).single() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -675,7 +667,6 @@ pub mod ts_milliseconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -703,7 +694,6 @@ pub mod ts_milliseconds {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1526522699, 918000000).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -798,7 +788,6 @@ pub mod ts_milliseconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -840,7 +829,6 @@ pub mod ts_milliseconds_option {
|
||||
/// assert_eq!(t, E::V(S { time: None }));
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -942,7 +930,6 @@ pub mod ts_seconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -970,7 +957,6 @@ pub mod ts_seconds {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1431684000, 0).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -1062,7 +1048,6 @@ pub mod ts_seconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -1093,7 +1078,6 @@ pub mod ts_seconds_option {
|
||||
/// assert_eq!(my_s, S { time: Utc.timestamp_opt(1431684000, 0).single() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -1136,30 +1120,36 @@ pub mod ts_seconds_option {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
super::test_encodable_json(serde_json::to_string, serde_json::to_string);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::datetime::{test_decodable_json, test_encodable_json};
|
||||
use crate::{DateTime, TimeZone, Utc};
|
||||
|
||||
#[cfg(feature = "clock")]
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
super::test_decodable_json(
|
||||
|input| serde_json::from_str(input),
|
||||
|input| serde_json::from_str(input),
|
||||
|input| serde_json::from_str(input),
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
test_encodable_json(serde_json::to_string, serde_json::to_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
#[cfg(feature = "clock")]
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
test_decodable_json(
|
||||
|input| serde_json::from_str(input),
|
||||
|input| serde_json::from_str(input),
|
||||
|input| serde_json::from_str(input),
|
||||
);
|
||||
}
|
||||
|
||||
let dt = Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap();
|
||||
let encoded = serialize(&dt).unwrap();
|
||||
let decoded: DateTime<Utc> = deserialize(&encoded).unwrap();
|
||||
assert_eq!(dt, decoded);
|
||||
assert_eq!(dt.offset(), decoded.offset());
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
let dt = Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap();
|
||||
let encoded = serialize(&dt).unwrap();
|
||||
let decoded: DateTime<Utc> = deserialize(&encoded).unwrap();
|
||||
assert_eq!(dt, decoded);
|
||||
assert_eq!(dt.offset(), decoded.offset());
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +240,7 @@ fn test_datetime_sub_months() {
|
||||
}
|
||||
|
||||
// local helper function to easily create a DateTime<FixedOffset>
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn ymdhms(
|
||||
fixedoffset: &FixedOffset,
|
||||
year: i32,
|
||||
@ -253,6 +254,7 @@ fn ymdhms(
|
||||
}
|
||||
|
||||
// local helper function to easily create a DateTime<FixedOffset>
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn ymdhms_milli(
|
||||
fixedoffset: &FixedOffset,
|
||||
year: i32,
|
||||
@ -271,6 +273,7 @@ fn ymdhms_milli(
|
||||
}
|
||||
|
||||
// local helper function to easily create a DateTime<FixedOffset>
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn ymdhms_micro(
|
||||
fixedoffset: &FixedOffset,
|
||||
year: i32,
|
||||
@ -289,6 +292,7 @@ fn ymdhms_micro(
|
||||
}
|
||||
|
||||
// local helper function to easily create a DateTime<FixedOffset>
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn ymdhms_nano(
|
||||
fixedoffset: &FixedOffset,
|
||||
year: i32,
|
||||
@ -400,6 +404,20 @@ fn test_datetime_offset() {
|
||||
assert!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset() != est);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::needless_borrow, clippy::op_ref)]
|
||||
fn signed_duration_since_autoref() {
|
||||
let dt1 = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
|
||||
let dt2 = Utc.with_ymd_and_hms(2014, 3, 4, 5, 6, 7).unwrap();
|
||||
let diff1 = dt1.signed_duration_since(dt2); // Copy/consume
|
||||
let diff2 = dt2.signed_duration_since(&dt1); // Take by reference
|
||||
assert_eq!(diff1, -diff2);
|
||||
|
||||
let diff1 = dt1 - &dt2; // We can choose to substract rhs by reference
|
||||
let diff2 = dt2 - dt1; // Or consume rhs
|
||||
assert_eq!(diff1, -diff2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_datetime_date_and_time() {
|
||||
let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap();
|
||||
@ -901,6 +919,10 @@ fn test_utc_datetime_from_str() {
|
||||
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(),
|
||||
NaiveDateTime::from_timestamp_opt(0, 0).unwrap().and_utc().fixed_offset()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
|
||||
@ -1549,3 +1571,31 @@ fn test_datetime_fixed_offset() {
|
||||
let datetime_fixed = fixed_offset.from_local_datetime(&naivedatetime).unwrap();
|
||||
assert_eq!(datetime_fixed.fixed_offset(), datetime_fixed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_sub_months() {
|
||||
let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap());
|
||||
|
||||
let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
|
||||
assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap());
|
||||
|
||||
let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap());
|
||||
|
||||
let utc_dt = Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap();
|
||||
assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
|
||||
assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_conversion() {
|
||||
let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
|
||||
let cdt_dt = FixedOffset::west_opt(5 * 60 * 60)
|
||||
.unwrap()
|
||||
.with_ymd_and_hms(2018, 9, 5, 18, 58, 0)
|
||||
.unwrap();
|
||||
let utc_dt2: DateTime<Utc> = cdt_dt.into();
|
||||
assert_eq!(utc_dt, utc_dt2);
|
||||
}
|
||||
|
@ -989,12 +989,12 @@ impl FromStr for Weekday {
|
||||
/// Formats single formatting item
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
|
||||
pub fn format_item_localized<'a>(
|
||||
pub fn format_item_localized(
|
||||
w: &mut fmt::Formatter,
|
||||
date: Option<&NaiveDate>,
|
||||
time: Option<&NaiveTime>,
|
||||
off: Option<&(String, FixedOffset)>,
|
||||
item: &Item<'a>,
|
||||
item: &Item<'_>,
|
||||
locale: Locale,
|
||||
) -> fmt::Result {
|
||||
let mut result = String::new();
|
||||
|
@ -620,7 +620,12 @@ impl Parsed {
|
||||
/// plus a time zone offset.
|
||||
/// Either way those fields have to be consistent to each other.
|
||||
pub fn to_datetime(&self) -> ParseResult<DateTime<FixedOffset>> {
|
||||
let offset = self.offset.ok_or(NOT_ENOUGH)?;
|
||||
// If there is no explicit offset, consider a timestamp value as indication of a UTC value.
|
||||
let offset = match (self.offset, self.timestamp) {
|
||||
(Some(off), _) => off,
|
||||
(None, Some(_)) => 0, // UNIX timestamp may assume 0 offset
|
||||
(None, None) => return Err(NOT_ENOUGH),
|
||||
};
|
||||
let datetime = self.to_naive_datetime_with_offset(offset)?;
|
||||
let offset = FixedOffset::east_opt(offset).ok_or(OUT_OF_RANGE)?;
|
||||
|
||||
|
@ -414,98 +414,103 @@ enum CommentState {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_rfc2822_comments() {
|
||||
let testdata = [
|
||||
("", Err(TOO_SHORT)),
|
||||
(" ", Err(TOO_SHORT)),
|
||||
("x", Err(INVALID)),
|
||||
("(", Err(TOO_SHORT)),
|
||||
("()", Ok("")),
|
||||
(" \r\n\t()", Ok("")),
|
||||
("() ", Ok(" ")),
|
||||
("()z", Ok("z")),
|
||||
("(x)", Ok("")),
|
||||
("(())", Ok("")),
|
||||
("((()))", Ok("")),
|
||||
("(x(x(x)x)x)", Ok("")),
|
||||
("( x ( x ( x ) x ) x )", Ok("")),
|
||||
(r"(\)", Err(TOO_SHORT)),
|
||||
(r"(\()", Ok("")),
|
||||
(r"(\))", Ok("")),
|
||||
(r"(\\)", Ok("")),
|
||||
("(()())", Ok("")),
|
||||
("( x ( x ) x ( x ) x )", Ok("")),
|
||||
];
|
||||
mod tests {
|
||||
use super::{comment_2822, consume_colon_maybe, s_next, space, trim1};
|
||||
use crate::format::{INVALID, TOO_SHORT};
|
||||
|
||||
for (test_in, expected) in testdata.iter() {
|
||||
let actual = comment_2822(test_in).map(|(s, _)| s);
|
||||
assert_eq!(
|
||||
*expected, actual,
|
||||
"{:?} expected to produce {:?}, but produced {:?}.",
|
||||
test_in, expected, actual
|
||||
);
|
||||
#[test]
|
||||
fn test_rfc2822_comments() {
|
||||
let testdata = [
|
||||
("", Err(TOO_SHORT)),
|
||||
(" ", Err(TOO_SHORT)),
|
||||
("x", Err(INVALID)),
|
||||
("(", Err(TOO_SHORT)),
|
||||
("()", Ok("")),
|
||||
(" \r\n\t()", Ok("")),
|
||||
("() ", Ok(" ")),
|
||||
("()z", Ok("z")),
|
||||
("(x)", Ok("")),
|
||||
("(())", Ok("")),
|
||||
("((()))", Ok("")),
|
||||
("(x(x(x)x)x)", Ok("")),
|
||||
("( x ( x ( x ) x ) x )", Ok("")),
|
||||
(r"(\)", Err(TOO_SHORT)),
|
||||
(r"(\()", Ok("")),
|
||||
(r"(\))", Ok("")),
|
||||
(r"(\\)", Ok("")),
|
||||
("(()())", Ok("")),
|
||||
("( x ( x ) x ( x ) x )", Ok("")),
|
||||
];
|
||||
|
||||
for (test_in, expected) in testdata.iter() {
|
||||
let actual = comment_2822(test_in).map(|(s, _)| s);
|
||||
assert_eq!(
|
||||
*expected, actual,
|
||||
"{:?} expected to produce {:?}, but produced {:?}.",
|
||||
test_in, expected, actual
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_space() {
|
||||
assert_eq!(space(""), Err(TOO_SHORT));
|
||||
assert_eq!(space(" "), Ok(""));
|
||||
assert_eq!(space(" \t"), Ok(""));
|
||||
assert_eq!(space(" \ta"), Ok("a"));
|
||||
assert_eq!(space(" \ta "), Ok("a "));
|
||||
assert_eq!(space("a"), Err(INVALID));
|
||||
assert_eq!(space("a "), Err(INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s_next() {
|
||||
assert_eq!(s_next(""), "");
|
||||
assert_eq!(s_next(" "), "");
|
||||
assert_eq!(s_next("a"), "");
|
||||
assert_eq!(s_next("ab"), "b");
|
||||
assert_eq!(s_next("abc"), "bc");
|
||||
assert_eq!(s_next("😾b"), "b");
|
||||
assert_eq!(s_next("a😾"), "😾");
|
||||
assert_eq!(s_next("😾bc"), "bc");
|
||||
assert_eq!(s_next("a😾c"), "😾c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trim1() {
|
||||
assert_eq!(trim1(""), "");
|
||||
assert_eq!(trim1(" "), "");
|
||||
assert_eq!(trim1("\t"), "");
|
||||
assert_eq!(trim1("\t\t"), "\t");
|
||||
assert_eq!(trim1(" "), " ");
|
||||
assert_eq!(trim1("a"), "a");
|
||||
assert_eq!(trim1("a "), "a ");
|
||||
assert_eq!(trim1("ab"), "ab");
|
||||
assert_eq!(trim1("😼"), "😼");
|
||||
assert_eq!(trim1("😼b"), "😼b");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consume_colon_maybe() {
|
||||
assert_eq!(consume_colon_maybe(""), Ok(""));
|
||||
assert_eq!(consume_colon_maybe(" "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe("\n"), Ok("\n"));
|
||||
assert_eq!(consume_colon_maybe(" "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe(":"), Ok(""));
|
||||
assert_eq!(consume_colon_maybe(" :"), Ok(" :"));
|
||||
assert_eq!(consume_colon_maybe(": "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe(" : "), Ok(" : "));
|
||||
assert_eq!(consume_colon_maybe(": "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe(" :"), Ok(" :"));
|
||||
assert_eq!(consume_colon_maybe(":: "), Ok(": "));
|
||||
assert_eq!(consume_colon_maybe("😸"), Ok("😸"));
|
||||
assert_eq!(consume_colon_maybe("😸😸"), Ok("😸😸"));
|
||||
assert_eq!(consume_colon_maybe("😸:"), Ok("😸:"));
|
||||
assert_eq!(consume_colon_maybe("😸 "), Ok("😸 "));
|
||||
assert_eq!(consume_colon_maybe(":😸"), Ok("😸"));
|
||||
assert_eq!(consume_colon_maybe(":😸 "), Ok("😸 "));
|
||||
assert_eq!(consume_colon_maybe(": 😸"), Ok(" 😸"));
|
||||
assert_eq!(consume_colon_maybe(": 😸"), Ok(" 😸"));
|
||||
assert_eq!(consume_colon_maybe(": :😸"), Ok(" :😸"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_space() {
|
||||
assert_eq!(space(""), Err(TOO_SHORT));
|
||||
assert_eq!(space(" "), Ok(""));
|
||||
assert_eq!(space(" \t"), Ok(""));
|
||||
assert_eq!(space(" \ta"), Ok("a"));
|
||||
assert_eq!(space(" \ta "), Ok("a "));
|
||||
assert_eq!(space("a"), Err(INVALID));
|
||||
assert_eq!(space("a "), Err(INVALID));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s_next() {
|
||||
assert_eq!(s_next(""), "");
|
||||
assert_eq!(s_next(" "), "");
|
||||
assert_eq!(s_next("a"), "");
|
||||
assert_eq!(s_next("ab"), "b");
|
||||
assert_eq!(s_next("abc"), "bc");
|
||||
assert_eq!(s_next("😾b"), "b");
|
||||
assert_eq!(s_next("a😾"), "😾");
|
||||
assert_eq!(s_next("😾bc"), "bc");
|
||||
assert_eq!(s_next("a😾c"), "😾c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_trim1() {
|
||||
assert_eq!(trim1(""), "");
|
||||
assert_eq!(trim1(" "), "");
|
||||
assert_eq!(trim1("\t"), "");
|
||||
assert_eq!(trim1("\t\t"), "\t");
|
||||
assert_eq!(trim1(" "), " ");
|
||||
assert_eq!(trim1("a"), "a");
|
||||
assert_eq!(trim1("a "), "a ");
|
||||
assert_eq!(trim1("ab"), "ab");
|
||||
assert_eq!(trim1("😼"), "😼");
|
||||
assert_eq!(trim1("😼b"), "😼b");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_consume_colon_maybe() {
|
||||
assert_eq!(consume_colon_maybe(""), Ok(""));
|
||||
assert_eq!(consume_colon_maybe(" "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe("\n"), Ok("\n"));
|
||||
assert_eq!(consume_colon_maybe(" "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe(":"), Ok(""));
|
||||
assert_eq!(consume_colon_maybe(" :"), Ok(" :"));
|
||||
assert_eq!(consume_colon_maybe(": "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe(" : "), Ok(" : "));
|
||||
assert_eq!(consume_colon_maybe(": "), Ok(" "));
|
||||
assert_eq!(consume_colon_maybe(" :"), Ok(" :"));
|
||||
assert_eq!(consume_colon_maybe(":: "), Ok(": "));
|
||||
assert_eq!(consume_colon_maybe("😸"), Ok("😸"));
|
||||
assert_eq!(consume_colon_maybe("😸😸"), Ok("😸😸"));
|
||||
assert_eq!(consume_colon_maybe("😸:"), Ok("😸:"));
|
||||
assert_eq!(consume_colon_maybe("😸 "), Ok("😸 "));
|
||||
assert_eq!(consume_colon_maybe(":😸"), Ok("😸"));
|
||||
assert_eq!(consume_colon_maybe(":😸 "), Ok("😸 "));
|
||||
assert_eq!(consume_colon_maybe(": 😸"), Ok(" 😸"));
|
||||
assert_eq!(consume_colon_maybe(": 😸"), Ok(" 😸"));
|
||||
assert_eq!(consume_colon_maybe(": :😸"), Ok(" :😸"));
|
||||
}
|
||||
|
@ -509,293 +509,301 @@ impl<'a> Iterator for StrftimeItems<'a> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_strftime_items() {
|
||||
fn parse_and_collect(s: &str) -> Vec<Item<'_>> {
|
||||
// map any error into `[Item::Error]`. useful for easy testing.
|
||||
eprintln!("test_strftime_items: parse_and_collect({:?})", s);
|
||||
let items = StrftimeItems::new(s);
|
||||
let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) });
|
||||
items.collect::<Option<Vec<_>>>().unwrap_or_else(|| vec![Item::Error])
|
||||
mod tests {
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
use super::Locale;
|
||||
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad, StrftimeItems};
|
||||
use crate::{DateTime, FixedOffset, NaiveDate, TimeZone, Timelike, Utc};
|
||||
|
||||
#[test]
|
||||
fn test_strftime_items() {
|
||||
fn parse_and_collect(s: &str) -> Vec<Item<'_>> {
|
||||
// map any error into `[Item::Error]`. useful for easy testing.
|
||||
eprintln!("test_strftime_items: parse_and_collect({:?})", s);
|
||||
let items = StrftimeItems::new(s);
|
||||
let items = items.map(|spec| if spec == Item::Error { None } else { Some(spec) });
|
||||
items.collect::<Option<Vec<_>>>().unwrap_or_else(|| vec![Item::Error])
|
||||
}
|
||||
|
||||
assert_eq!(parse_and_collect(""), []);
|
||||
assert_eq!(parse_and_collect(" "), [sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" "), [sp!(" ")]);
|
||||
// ne!
|
||||
assert_ne!(parse_and_collect(" "), [sp!(" "), sp!(" ")]);
|
||||
// eq!
|
||||
assert_eq!(parse_and_collect(" "), [sp!(" ")]);
|
||||
assert_eq!(parse_and_collect("a"), [lit!("a")]);
|
||||
assert_eq!(parse_and_collect("ab"), [lit!("ab")]);
|
||||
assert_eq!(parse_and_collect("😽"), [lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("a😽"), [lit!("a😽")]);
|
||||
assert_eq!(parse_and_collect("😽a"), [lit!("😽a")]);
|
||||
assert_eq!(parse_and_collect(" 😽"), [sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("😽 "), [lit!("😽"), sp!(" ")]);
|
||||
// ne!
|
||||
assert_ne!(parse_and_collect("😽😽"), [lit!("😽")]);
|
||||
assert_ne!(parse_and_collect("😽"), [lit!("😽😽")]);
|
||||
assert_ne!(parse_and_collect("😽😽"), [lit!("😽😽"), lit!("😽")]);
|
||||
// eq!
|
||||
assert_eq!(parse_and_collect("😽😽"), [lit!("😽😽")]);
|
||||
assert_eq!(parse_and_collect(" \t\n\r "), [sp!(" \t\n\r ")]);
|
||||
assert_eq!(parse_and_collect("hello?"), [lit!("hello?")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("a b\t\nc"),
|
||||
[lit!("a"), sp!(" "), lit!("b"), sp!("\t\n"), lit!("c")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("100%%"), [lit!("100"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("100%% ok"), [lit!("100"), lit!("%"), sp!(" "), lit!("ok")]);
|
||||
assert_eq!(parse_and_collect("%%PDF-1.0"), [lit!("%"), lit!("PDF-1.0")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("%Y-%m-%d"),
|
||||
[num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)]
|
||||
);
|
||||
assert_eq!(parse_and_collect("😽 "), [lit!("😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect("😽😽"), [lit!("😽😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽😽"), [lit!("😽😽😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽 😽"), [lit!("😽😽"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽a 😽"), [lit!("😽😽a"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽a b😽"), [lit!("😽😽a"), sp!(" "), lit!("b😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽a b😽c"), [lit!("😽😽a"), sp!(" "), lit!("b😽c")]);
|
||||
assert_eq!(parse_and_collect("😽😽 "), [lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect("😽😽 😽"), [lit!("😽😽"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect(" 😽"), [sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect(" 😽 "), [sp!(" "), lit!("😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽 😽"), [sp!(" "), lit!("😽"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(parse_and_collect(" 😽😽"), [sp!(" "), lit!("😽😽")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽はい😽 ハンバーガー"),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽はい😽"), sp!(" "), lit!("ハンバーガー")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("%%😽%%😽"), [lit!("%"), lit!("😽"), lit!("%"), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("%Y--%m"), [num0!(Year), lit!("--"), num0!(Month)]);
|
||||
assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]"));
|
||||
assert_eq!(parse_and_collect("100%%😽"), [lit!("100"), lit!("%"), lit!("😽")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("100%%😽%%a"),
|
||||
[lit!("100"), lit!("%"), lit!("😽"), lit!("%"), lit!("a")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("😽100%%"), [lit!("😽100"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("%m %d"), [num0!(Month), sp!(" "), num0!(Day)]);
|
||||
assert_eq!(parse_and_collect("%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%%"), [lit!("%")]);
|
||||
assert_eq!(parse_and_collect("%%%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%a"), [fix!(ShortWeekdayName)]);
|
||||
assert_eq!(parse_and_collect("%aa"), [fix!(ShortWeekdayName), lit!("a")]);
|
||||
assert_eq!(parse_and_collect("%%a%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%😽"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%😽😽"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%%%%"), [lit!("%"), lit!("%")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("%%%%ハンバーガー"),
|
||||
[lit!("%"), lit!("%"), lit!("ハンバーガー")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("foo%?"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("bar%42"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("quux% +"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%.Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%0Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%_Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%.j"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:j"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-j"), [num!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%0j"), [num0!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%_j"), [nums!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%.e"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:e"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-e"), [num!(Day)]);
|
||||
assert_eq!(parse_and_collect("%0e"), [num0!(Day)]);
|
||||
assert_eq!(parse_and_collect("%_e"), [nums!(Day)]);
|
||||
assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]);
|
||||
assert_eq!(parse_and_collect("%:z"), [fix!(TimezoneOffsetColon)]);
|
||||
assert_eq!(parse_and_collect("%Z"), [fix!(TimezoneName)]);
|
||||
assert_eq!(parse_and_collect("%ZZZZ"), [fix!(TimezoneName), lit!("ZZZ")]);
|
||||
assert_eq!(parse_and_collect("%Z😽"), [fix!(TimezoneName), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]);
|
||||
assert_eq!(parse_and_collect("%#m"), [Item::Error]);
|
||||
}
|
||||
|
||||
assert_eq!(parse_and_collect(""), []);
|
||||
assert_eq!(parse_and_collect(" "), [sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" "), [sp!(" ")]);
|
||||
// ne!
|
||||
assert_ne!(parse_and_collect(" "), [sp!(" "), sp!(" ")]);
|
||||
// eq!
|
||||
assert_eq!(parse_and_collect(" "), [sp!(" ")]);
|
||||
assert_eq!(parse_and_collect("a"), [lit!("a")]);
|
||||
assert_eq!(parse_and_collect("ab"), [lit!("ab")]);
|
||||
assert_eq!(parse_and_collect("😽"), [lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("a😽"), [lit!("a😽")]);
|
||||
assert_eq!(parse_and_collect("😽a"), [lit!("😽a")]);
|
||||
assert_eq!(parse_and_collect(" 😽"), [sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("😽 "), [lit!("😽"), sp!(" ")]);
|
||||
// ne!
|
||||
assert_ne!(parse_and_collect("😽😽"), [lit!("😽")]);
|
||||
assert_ne!(parse_and_collect("😽"), [lit!("😽😽")]);
|
||||
assert_ne!(parse_and_collect("😽😽"), [lit!("😽😽"), lit!("😽")]);
|
||||
// eq!
|
||||
assert_eq!(parse_and_collect("😽😽"), [lit!("😽😽")]);
|
||||
assert_eq!(parse_and_collect(" \t\n\r "), [sp!(" \t\n\r ")]);
|
||||
assert_eq!(parse_and_collect("hello?"), [lit!("hello?")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("a b\t\nc"),
|
||||
[lit!("a"), sp!(" "), lit!("b"), sp!("\t\n"), lit!("c")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("100%%"), [lit!("100"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("100%% ok"), [lit!("100"), lit!("%"), sp!(" "), lit!("ok")]);
|
||||
assert_eq!(parse_and_collect("%%PDF-1.0"), [lit!("%"), lit!("PDF-1.0")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("%Y-%m-%d"),
|
||||
[num0!(Year), lit!("-"), num0!(Month), lit!("-"), num0!(Day)]
|
||||
);
|
||||
assert_eq!(parse_and_collect("😽 "), [lit!("😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect("😽😽"), [lit!("😽😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽😽"), [lit!("😽😽😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽 😽"), [lit!("😽😽"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽a 😽"), [lit!("😽😽a"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽a b😽"), [lit!("😽😽a"), sp!(" "), lit!("b😽")]);
|
||||
assert_eq!(parse_and_collect("😽😽a b😽c"), [lit!("😽😽a"), sp!(" "), lit!("b😽c")]);
|
||||
assert_eq!(parse_and_collect("😽😽 "), [lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect("😽😽 😽"), [lit!("😽😽"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect(" 😽"), [sp!(" "), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect(" 😽 "), [sp!(" "), lit!("😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽 😽"), [sp!(" "), lit!("😽"), sp!(" "), lit!("😽")]);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(parse_and_collect(" 😽😽"), [sp!(" "), lit!("😽😽")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(parse_and_collect(" 😽😽 "), [sp!(" "), lit!("😽😽"), sp!(" ")]);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽😽 "),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽😽"), sp!(" ")]
|
||||
);
|
||||
assert_eq!(
|
||||
parse_and_collect(" 😽 😽はい😽 ハンバーガー"),
|
||||
[sp!(" "), lit!("😽"), sp!(" "), lit!("😽はい😽"), sp!(" "), lit!("ハンバーガー")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("%%😽%%😽"), [lit!("%"), lit!("😽"), lit!("%"), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("%Y--%m"), [num0!(Year), lit!("--"), num0!(Month)]);
|
||||
assert_eq!(parse_and_collect("[%F]"), parse_and_collect("[%Y-%m-%d]"));
|
||||
assert_eq!(parse_and_collect("100%%😽"), [lit!("100"), lit!("%"), lit!("😽")]);
|
||||
assert_eq!(
|
||||
parse_and_collect("100%%😽%%a"),
|
||||
[lit!("100"), lit!("%"), lit!("😽"), lit!("%"), lit!("a")]
|
||||
);
|
||||
assert_eq!(parse_and_collect("😽100%%"), [lit!("😽100"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("%m %d"), [num0!(Month), sp!(" "), num0!(Day)]);
|
||||
assert_eq!(parse_and_collect("%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%%"), [lit!("%")]);
|
||||
assert_eq!(parse_and_collect("%%%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%a"), [fix!(ShortWeekdayName)]);
|
||||
assert_eq!(parse_and_collect("%aa"), [fix!(ShortWeekdayName), lit!("a")]);
|
||||
assert_eq!(parse_and_collect("%%a%"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%😽"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%😽😽"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%%%%"), [lit!("%"), lit!("%")]);
|
||||
assert_eq!(parse_and_collect("%%%%ハンバーガー"), [lit!("%"), lit!("%"), lit!("ハンバーガー")]);
|
||||
assert_eq!(parse_and_collect("foo%?"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("bar%42"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("quux% +"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%.Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%0Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%_Z"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%.j"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:j"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-j"), [num!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%0j"), [num0!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%_j"), [nums!(Ordinal)]);
|
||||
assert_eq!(parse_and_collect("%.e"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%:e"), [Item::Error]);
|
||||
assert_eq!(parse_and_collect("%-e"), [num!(Day)]);
|
||||
assert_eq!(parse_and_collect("%0e"), [num0!(Day)]);
|
||||
assert_eq!(parse_and_collect("%_e"), [nums!(Day)]);
|
||||
assert_eq!(parse_and_collect("%z"), [fix!(TimezoneOffset)]);
|
||||
assert_eq!(parse_and_collect("%:z"), [fix!(TimezoneOffsetColon)]);
|
||||
assert_eq!(parse_and_collect("%Z"), [fix!(TimezoneName)]);
|
||||
assert_eq!(parse_and_collect("%ZZZZ"), [fix!(TimezoneName), lit!("ZZZ")]);
|
||||
assert_eq!(parse_and_collect("%Z😽"), [fix!(TimezoneName), lit!("😽")]);
|
||||
assert_eq!(parse_and_collect("%#z"), [internal_fix!(TimezoneOffsetPermissive)]);
|
||||
assert_eq!(parse_and_collect("%#m"), [Item::Error]);
|
||||
}
|
||||
#[test]
|
||||
fn test_strftime_docs() {
|
||||
let dt = FixedOffset::east_opt(34200)
|
||||
.unwrap()
|
||||
.from_local_datetime(
|
||||
&NaiveDate::from_ymd_opt(2001, 7, 8)
|
||||
.unwrap()
|
||||
.and_hms_nano_opt(0, 34, 59, 1_026_490_708)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_strftime_docs() {
|
||||
use crate::NaiveDate;
|
||||
use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc};
|
||||
// date specifiers
|
||||
assert_eq!(dt.format("%Y").to_string(), "2001");
|
||||
assert_eq!(dt.format("%C").to_string(), "20");
|
||||
assert_eq!(dt.format("%y").to_string(), "01");
|
||||
assert_eq!(dt.format("%m").to_string(), "07");
|
||||
assert_eq!(dt.format("%b").to_string(), "Jul");
|
||||
assert_eq!(dt.format("%B").to_string(), "July");
|
||||
assert_eq!(dt.format("%h").to_string(), "Jul");
|
||||
assert_eq!(dt.format("%d").to_string(), "08");
|
||||
assert_eq!(dt.format("%e").to_string(), " 8");
|
||||
assert_eq!(dt.format("%e").to_string(), dt.format("%_d").to_string());
|
||||
assert_eq!(dt.format("%a").to_string(), "Sun");
|
||||
assert_eq!(dt.format("%A").to_string(), "Sunday");
|
||||
assert_eq!(dt.format("%w").to_string(), "0");
|
||||
assert_eq!(dt.format("%u").to_string(), "7");
|
||||
assert_eq!(dt.format("%U").to_string(), "27");
|
||||
assert_eq!(dt.format("%W").to_string(), "27");
|
||||
assert_eq!(dt.format("%G").to_string(), "2001");
|
||||
assert_eq!(dt.format("%g").to_string(), "01");
|
||||
assert_eq!(dt.format("%V").to_string(), "27");
|
||||
assert_eq!(dt.format("%j").to_string(), "189");
|
||||
assert_eq!(dt.format("%D").to_string(), "07/08/01");
|
||||
assert_eq!(dt.format("%x").to_string(), "07/08/01");
|
||||
assert_eq!(dt.format("%F").to_string(), "2001-07-08");
|
||||
assert_eq!(dt.format("%v").to_string(), " 8-Jul-2001");
|
||||
|
||||
let dt = FixedOffset::east_opt(34200)
|
||||
.unwrap()
|
||||
.from_local_datetime(
|
||||
&NaiveDate::from_ymd_opt(2001, 7, 8)
|
||||
// time specifiers
|
||||
assert_eq!(dt.format("%H").to_string(), "00");
|
||||
assert_eq!(dt.format("%k").to_string(), " 0");
|
||||
assert_eq!(dt.format("%k").to_string(), dt.format("%_H").to_string());
|
||||
assert_eq!(dt.format("%I").to_string(), "12");
|
||||
assert_eq!(dt.format("%l").to_string(), "12");
|
||||
assert_eq!(dt.format("%l").to_string(), dt.format("%_I").to_string());
|
||||
assert_eq!(dt.format("%P").to_string(), "am");
|
||||
assert_eq!(dt.format("%p").to_string(), "AM");
|
||||
assert_eq!(dt.format("%M").to_string(), "34");
|
||||
assert_eq!(dt.format("%S").to_string(), "60");
|
||||
assert_eq!(dt.format("%f").to_string(), "026490708");
|
||||
assert_eq!(dt.format("%.f").to_string(), ".026490708");
|
||||
assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(), ".026490");
|
||||
assert_eq!(dt.format("%.3f").to_string(), ".026");
|
||||
assert_eq!(dt.format("%.6f").to_string(), ".026490");
|
||||
assert_eq!(dt.format("%.9f").to_string(), ".026490708");
|
||||
assert_eq!(dt.format("%3f").to_string(), "026");
|
||||
assert_eq!(dt.format("%6f").to_string(), "026490");
|
||||
assert_eq!(dt.format("%9f").to_string(), "026490708");
|
||||
assert_eq!(dt.format("%R").to_string(), "00:34");
|
||||
assert_eq!(dt.format("%T").to_string(), "00:34:60");
|
||||
assert_eq!(dt.format("%X").to_string(), "00:34:60");
|
||||
assert_eq!(dt.format("%r").to_string(), "12:34:60 AM");
|
||||
|
||||
// time zone specifiers
|
||||
//assert_eq!(dt.format("%Z").to_string(), "ACST");
|
||||
assert_eq!(dt.format("%z").to_string(), "+0930");
|
||||
assert_eq!(dt.format("%:z").to_string(), "+09:30");
|
||||
assert_eq!(dt.format("%::z").to_string(), "+09:30:00");
|
||||
assert_eq!(dt.format("%:::z").to_string(), "+09");
|
||||
|
||||
// date & time specifiers
|
||||
assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001");
|
||||
assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30");
|
||||
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc).format("%+").to_string(),
|
||||
"2001-07-07T15:04:60.026490708+00:00"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc),
|
||||
DateTime::<FixedOffset>::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+")
|
||||
.unwrap()
|
||||
.and_hms_nano_opt(0, 34, 59, 1_026_490_708)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
);
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc),
|
||||
DateTime::<FixedOffset>::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+")
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc),
|
||||
DateTime::<FixedOffset>::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+")
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
// date specifiers
|
||||
assert_eq!(dt.format("%Y").to_string(), "2001");
|
||||
assert_eq!(dt.format("%C").to_string(), "20");
|
||||
assert_eq!(dt.format("%y").to_string(), "01");
|
||||
assert_eq!(dt.format("%m").to_string(), "07");
|
||||
assert_eq!(dt.format("%b").to_string(), "Jul");
|
||||
assert_eq!(dt.format("%B").to_string(), "July");
|
||||
assert_eq!(dt.format("%h").to_string(), "Jul");
|
||||
assert_eq!(dt.format("%d").to_string(), "08");
|
||||
assert_eq!(dt.format("%e").to_string(), " 8");
|
||||
assert_eq!(dt.format("%e").to_string(), dt.format("%_d").to_string());
|
||||
assert_eq!(dt.format("%a").to_string(), "Sun");
|
||||
assert_eq!(dt.format("%A").to_string(), "Sunday");
|
||||
assert_eq!(dt.format("%w").to_string(), "0");
|
||||
assert_eq!(dt.format("%u").to_string(), "7");
|
||||
assert_eq!(dt.format("%U").to_string(), "27");
|
||||
assert_eq!(dt.format("%W").to_string(), "27");
|
||||
assert_eq!(dt.format("%G").to_string(), "2001");
|
||||
assert_eq!(dt.format("%g").to_string(), "01");
|
||||
assert_eq!(dt.format("%V").to_string(), "27");
|
||||
assert_eq!(dt.format("%j").to_string(), "189");
|
||||
assert_eq!(dt.format("%D").to_string(), "07/08/01");
|
||||
assert_eq!(dt.format("%x").to_string(), "07/08/01");
|
||||
assert_eq!(dt.format("%F").to_string(), "2001-07-08");
|
||||
assert_eq!(dt.format("%v").to_string(), " 8-Jul-2001");
|
||||
assert_eq!(
|
||||
dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(),
|
||||
"2001-07-08T00:34:60.026490+09:30"
|
||||
);
|
||||
assert_eq!(dt.format("%s").to_string(), "994518299");
|
||||
|
||||
// time specifiers
|
||||
assert_eq!(dt.format("%H").to_string(), "00");
|
||||
assert_eq!(dt.format("%k").to_string(), " 0");
|
||||
assert_eq!(dt.format("%k").to_string(), dt.format("%_H").to_string());
|
||||
assert_eq!(dt.format("%I").to_string(), "12");
|
||||
assert_eq!(dt.format("%l").to_string(), "12");
|
||||
assert_eq!(dt.format("%l").to_string(), dt.format("%_I").to_string());
|
||||
assert_eq!(dt.format("%P").to_string(), "am");
|
||||
assert_eq!(dt.format("%p").to_string(), "AM");
|
||||
assert_eq!(dt.format("%M").to_string(), "34");
|
||||
assert_eq!(dt.format("%S").to_string(), "60");
|
||||
assert_eq!(dt.format("%f").to_string(), "026490708");
|
||||
assert_eq!(dt.format("%.f").to_string(), ".026490708");
|
||||
assert_eq!(dt.with_nanosecond(1_026_490_000).unwrap().format("%.f").to_string(), ".026490");
|
||||
assert_eq!(dt.format("%.3f").to_string(), ".026");
|
||||
assert_eq!(dt.format("%.6f").to_string(), ".026490");
|
||||
assert_eq!(dt.format("%.9f").to_string(), ".026490708");
|
||||
assert_eq!(dt.format("%3f").to_string(), "026");
|
||||
assert_eq!(dt.format("%6f").to_string(), "026490");
|
||||
assert_eq!(dt.format("%9f").to_string(), "026490708");
|
||||
assert_eq!(dt.format("%R").to_string(), "00:34");
|
||||
assert_eq!(dt.format("%T").to_string(), "00:34:60");
|
||||
assert_eq!(dt.format("%X").to_string(), "00:34:60");
|
||||
assert_eq!(dt.format("%r").to_string(), "12:34:60 AM");
|
||||
// special specifiers
|
||||
assert_eq!(dt.format("%t").to_string(), "\t");
|
||||
assert_eq!(dt.format("%n").to_string(), "\n");
|
||||
assert_eq!(dt.format("%%").to_string(), "%");
|
||||
|
||||
// time zone specifiers
|
||||
//assert_eq!(dt.format("%Z").to_string(), "ACST");
|
||||
assert_eq!(dt.format("%z").to_string(), "+0930");
|
||||
assert_eq!(dt.format("%:z").to_string(), "+09:30");
|
||||
assert_eq!(dt.format("%::z").to_string(), "+09:30:00");
|
||||
assert_eq!(dt.format("%:::z").to_string(), "+09");
|
||||
// complex format specifiers
|
||||
assert_eq!(dt.format(" %Y%d%m%%%%%t%H%M%S\t").to_string(), " 20010807%%\t003460\t");
|
||||
assert_eq!(
|
||||
dt.format(" %Y%d%m%%%%%t%H:%P:%M%S%:::z\t").to_string(),
|
||||
" 20010807%%\t00:am:3460+09\t"
|
||||
);
|
||||
}
|
||||
|
||||
// date & time specifiers
|
||||
assert_eq!(dt.format("%c").to_string(), "Sun Jul 8 00:34:60 2001");
|
||||
assert_eq!(dt.format("%+").to_string(), "2001-07-08T00:34:60.026490708+09:30");
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[test]
|
||||
fn test_strftime_docs_localized() {
|
||||
let dt = FixedOffset::east_opt(34200)
|
||||
.unwrap()
|
||||
.with_ymd_and_hms(2001, 7, 8, 0, 34, 59)
|
||||
.unwrap()
|
||||
.with_nanosecond(1_026_490_708)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc).format("%+").to_string(),
|
||||
"2001-07-07T15:04:60.026490708+00:00"
|
||||
);
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc),
|
||||
DateTime::<FixedOffset>::parse_from_str("2001-07-07T15:04:60.026490708Z", "%+").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc),
|
||||
DateTime::<FixedOffset>::parse_from_str("2001-07-07T15:04:60.026490708UTC", "%+").unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
dt.with_timezone(&Utc),
|
||||
DateTime::<FixedOffset>::parse_from_str("2001-07-07t15:04:60.026490708utc", "%+").unwrap()
|
||||
);
|
||||
// date specifiers
|
||||
assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui");
|
||||
assert_eq!(dt.format_localized("%B", Locale::fr_BE).to_string(), "juillet");
|
||||
assert_eq!(dt.format_localized("%h", Locale::fr_BE).to_string(), "jui");
|
||||
assert_eq!(dt.format_localized("%a", Locale::fr_BE).to_string(), "dim");
|
||||
assert_eq!(dt.format_localized("%A", Locale::fr_BE).to_string(), "dimanche");
|
||||
assert_eq!(dt.format_localized("%D", Locale::fr_BE).to_string(), "07/08/01");
|
||||
assert_eq!(dt.format_localized("%x", Locale::fr_BE).to_string(), "08/07/01");
|
||||
assert_eq!(dt.format_localized("%F", Locale::fr_BE).to_string(), "2001-07-08");
|
||||
assert_eq!(dt.format_localized("%v", Locale::fr_BE).to_string(), " 8-jui-2001");
|
||||
|
||||
assert_eq!(
|
||||
dt.with_nanosecond(1_026_490_000).unwrap().format("%+").to_string(),
|
||||
"2001-07-08T00:34:60.026490+09:30"
|
||||
);
|
||||
assert_eq!(dt.format("%s").to_string(), "994518299");
|
||||
// time specifiers
|
||||
assert_eq!(dt.format_localized("%P", Locale::fr_BE).to_string(), "");
|
||||
assert_eq!(dt.format_localized("%p", Locale::fr_BE).to_string(), "");
|
||||
assert_eq!(dt.format_localized("%R", Locale::fr_BE).to_string(), "00:34");
|
||||
assert_eq!(dt.format_localized("%T", Locale::fr_BE).to_string(), "00:34:60");
|
||||
assert_eq!(dt.format_localized("%X", Locale::fr_BE).to_string(), "00:34:60");
|
||||
assert_eq!(dt.format_localized("%r", Locale::fr_BE).to_string(), "12:34:60 ");
|
||||
|
||||
// special specifiers
|
||||
assert_eq!(dt.format("%t").to_string(), "\t");
|
||||
assert_eq!(dt.format("%n").to_string(), "\n");
|
||||
assert_eq!(dt.format("%%").to_string(), "%");
|
||||
// date & time specifiers
|
||||
assert_eq!(
|
||||
dt.format_localized("%c", Locale::fr_BE).to_string(),
|
||||
"dim 08 jui 2001 00:34:60 +09:30"
|
||||
);
|
||||
|
||||
// complex format specifiers
|
||||
assert_eq!(dt.format(" %Y%d%m%%%%%t%H%M%S\t").to_string(), " 20010807%%\t003460\t");
|
||||
assert_eq!(
|
||||
dt.format(" %Y%d%m%%%%%t%H:%P:%M%S%:::z\t").to_string(),
|
||||
" 20010807%%\t00:am:3460+09\t"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-locales")]
|
||||
#[test]
|
||||
fn test_strftime_docs_localized() {
|
||||
use crate::{FixedOffset, NaiveDate};
|
||||
|
||||
let dt = NaiveDate::from_ymd_opt(2001, 7, 8)
|
||||
.and_then(|d| d.and_hms_nano_opt(0, 34, 59, 1_026_490_708))
|
||||
.unwrap()
|
||||
.and_local_timezone(FixedOffset::east_opt(34200).unwrap())
|
||||
.unwrap();
|
||||
|
||||
// date specifiers
|
||||
assert_eq!(dt.format_localized("%b", Locale::fr_BE).to_string(), "jui");
|
||||
assert_eq!(dt.format_localized("%B", Locale::fr_BE).to_string(), "juillet");
|
||||
assert_eq!(dt.format_localized("%h", Locale::fr_BE).to_string(), "jui");
|
||||
assert_eq!(dt.format_localized("%a", Locale::fr_BE).to_string(), "dim");
|
||||
assert_eq!(dt.format_localized("%A", Locale::fr_BE).to_string(), "dimanche");
|
||||
assert_eq!(dt.format_localized("%D", Locale::fr_BE).to_string(), "07/08/01");
|
||||
assert_eq!(dt.format_localized("%x", Locale::fr_BE).to_string(), "08/07/01");
|
||||
assert_eq!(dt.format_localized("%F", Locale::fr_BE).to_string(), "2001-07-08");
|
||||
assert_eq!(dt.format_localized("%v", Locale::fr_BE).to_string(), " 8-jui-2001");
|
||||
|
||||
// time specifiers
|
||||
assert_eq!(dt.format_localized("%P", Locale::fr_BE).to_string(), "");
|
||||
assert_eq!(dt.format_localized("%p", Locale::fr_BE).to_string(), "");
|
||||
assert_eq!(dt.format_localized("%R", Locale::fr_BE).to_string(), "00:34");
|
||||
assert_eq!(dt.format_localized("%T", Locale::fr_BE).to_string(), "00:34:60");
|
||||
assert_eq!(dt.format_localized("%X", Locale::fr_BE).to_string(), "00:34:60");
|
||||
assert_eq!(dt.format_localized("%r", Locale::fr_BE).to_string(), "12:34:60 ");
|
||||
|
||||
// date & time specifiers
|
||||
assert_eq!(
|
||||
dt.format_localized("%c", Locale::fr_BE).to_string(),
|
||||
"dim 08 jui 2001 00:34:60 +09:30"
|
||||
);
|
||||
|
||||
let nd = NaiveDate::from_ymd_opt(2001, 7, 8).unwrap();
|
||||
|
||||
// date specifiers
|
||||
assert_eq!(nd.format_localized("%b", Locale::de_DE).to_string(), "Jul");
|
||||
assert_eq!(nd.format_localized("%B", Locale::de_DE).to_string(), "Juli");
|
||||
assert_eq!(nd.format_localized("%h", Locale::de_DE).to_string(), "Jul");
|
||||
assert_eq!(nd.format_localized("%a", Locale::de_DE).to_string(), "So");
|
||||
assert_eq!(nd.format_localized("%A", Locale::de_DE).to_string(), "Sonntag");
|
||||
assert_eq!(nd.format_localized("%D", Locale::de_DE).to_string(), "07/08/01");
|
||||
assert_eq!(nd.format_localized("%x", Locale::de_DE).to_string(), "08.07.2001");
|
||||
assert_eq!(nd.format_localized("%F", Locale::de_DE).to_string(), "2001-07-08");
|
||||
assert_eq!(nd.format_localized("%v", Locale::de_DE).to_string(), " 8-Jul-2001");
|
||||
let nd = NaiveDate::from_ymd_opt(2001, 7, 8).unwrap();
|
||||
|
||||
// date specifiers
|
||||
assert_eq!(nd.format_localized("%b", Locale::de_DE).to_string(), "Jul");
|
||||
assert_eq!(nd.format_localized("%B", Locale::de_DE).to_string(), "Juli");
|
||||
assert_eq!(nd.format_localized("%h", Locale::de_DE).to_string(), "Jul");
|
||||
assert_eq!(nd.format_localized("%a", Locale::de_DE).to_string(), "So");
|
||||
assert_eq!(nd.format_localized("%A", Locale::de_DE).to_string(), "Sonntag");
|
||||
assert_eq!(nd.format_localized("%D", Locale::de_DE).to_string(), "07/08/01");
|
||||
assert_eq!(nd.format_localized("%x", Locale::de_DE).to_string(), "08.07.2001");
|
||||
assert_eq!(nd.format_localized("%F", Locale::de_DE).to_string(), "2001-07-08");
|
||||
assert_eq!(nd.format_localized("%v", Locale::de_DE).to_string(), " 8-Jul-2001");
|
||||
}
|
||||
}
|
||||
|
@ -374,6 +374,7 @@
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![warn(unreachable_pub)]
|
||||
#![deny(dead_code)]
|
||||
#![deny(clippy::tests_outside_test_module)]
|
||||
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
|
82
src/month.rs
82
src/month.rs
@ -28,7 +28,7 @@ use crate::OutOfRange;
|
||||
/// Allows mapping from and to month, from 1-January to 12-December.
|
||||
/// Can be Serialized/Deserialized with serde
|
||||
// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior.
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd)]
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
pub enum Month {
|
||||
@ -180,7 +180,7 @@ impl TryFrom<u8> for Month {
|
||||
}
|
||||
|
||||
/// A duration in calendar months
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
pub struct Months(pub(crate) u32);
|
||||
|
||||
@ -245,8 +245,47 @@ mod month_serde {
|
||||
deserializer.deserialize_str(MonthVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Month;
|
||||
use crate::{Datelike, OutOfRange, TimeZone, Utc};
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_try_from() {
|
||||
assert_eq!(Month::try_from(1), Ok(Month::January));
|
||||
assert_eq!(Month::try_from(2), Ok(Month::February));
|
||||
assert_eq!(Month::try_from(12), Ok(Month::December));
|
||||
assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
|
||||
|
||||
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
|
||||
assert_eq!(Month::try_from(date.month() as u8), Ok(Month::October));
|
||||
|
||||
let month = Month::January;
|
||||
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
|
||||
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_succ_pred() {
|
||||
assert_eq!(Month::January.succ(), Month::February);
|
||||
assert_eq!(Month::December.succ(), Month::January);
|
||||
assert_eq!(Month::January.pred(), Month::December);
|
||||
assert_eq!(Month::February.pred(), Month::January);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_partial_ord() {
|
||||
assert!(Month::January <= Month::January);
|
||||
assert!(Month::January < Month::February);
|
||||
assert!(Month::January < Month::December);
|
||||
assert!(Month::July >= Month::May);
|
||||
assert!(Month::September > Month::March);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_serialize() {
|
||||
use serde_json::to_string;
|
||||
use Month::*;
|
||||
@ -273,6 +312,7 @@ mod month_serde {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_deserialize() {
|
||||
use serde_json::from_str;
|
||||
use Month::*;
|
||||
@ -307,41 +347,3 @@ mod month_serde {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Month;
|
||||
use crate::{Datelike, OutOfRange, TimeZone, Utc};
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_try_from() {
|
||||
assert_eq!(Month::try_from(1), Ok(Month::January));
|
||||
assert_eq!(Month::try_from(2), Ok(Month::February));
|
||||
assert_eq!(Month::try_from(12), Ok(Month::December));
|
||||
assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
|
||||
|
||||
let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
|
||||
assert_eq!(Month::try_from(date.month() as u8).ok(), Some(Month::October));
|
||||
|
||||
let month = Month::January;
|
||||
let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
|
||||
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_enum_succ_pred() {
|
||||
assert_eq!(Month::January.succ(), Month::February);
|
||||
assert_eq!(Month::December.succ(), Month::January);
|
||||
assert_eq!(Month::January.pred(), Month::December);
|
||||
assert_eq!(Month::February.pred(), Month::January);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_month_partial_ord() {
|
||||
assert!(Month::January <= Month::January);
|
||||
assert!(Month::January < Month::February);
|
||||
assert!(Month::January < Month::December);
|
||||
assert!(Month::July >= Month::May);
|
||||
assert!(Month::September > Month::March);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#[cfg(any(feature = "alloc", feature = "std", test))]
|
||||
use core::borrow::Borrow;
|
||||
use core::iter::FusedIterator;
|
||||
use core::ops::{Add, AddAssign, RangeInclusive, Sub, SubAssign};
|
||||
use core::{fmt, str};
|
||||
|
||||
@ -31,29 +32,6 @@ use super::isoweek;
|
||||
const MAX_YEAR: i32 = internals::MAX_YEAR;
|
||||
const MIN_YEAR: i32 = internals::MIN_YEAR;
|
||||
|
||||
// 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
|
||||
// = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365 days
|
||||
#[cfg(test)] // only used for testing
|
||||
const MAX_DAYS_FROM_YEAR_0: i32 =
|
||||
MAX_YEAR * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400 + 365;
|
||||
|
||||
// MIN_YEAR-01-01 minus 0000-01-01
|
||||
// = (MIN_YEAR+400n+1)-01-01 minus (400n+1)-01-01
|
||||
// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - ((400n+1)-01-01 minus 0001-01-01)
|
||||
// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - 146097n days
|
||||
//
|
||||
// n is set to 1000 for convenience.
|
||||
#[cfg(test)] // only used for testing
|
||||
const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 + (MIN_YEAR + 400_000) / 4
|
||||
- (MIN_YEAR + 400_000) / 100
|
||||
+ (MIN_YEAR + 400_000) / 400
|
||||
- 146_097_000;
|
||||
|
||||
#[cfg(test)] // only used for testing, but duplicated in naive::datetime
|
||||
const MAX_BITS: usize = 44;
|
||||
|
||||
/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
|
||||
/// day of the week.
|
||||
#[derive(Debug)]
|
||||
@ -65,6 +43,11 @@ pub struct NaiveWeek {
|
||||
impl NaiveWeek {
|
||||
/// Returns a date representing the first day of the week.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the first day of the week happens to fall just out of range of `NaiveDate`
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -88,6 +71,11 @@ impl NaiveWeek {
|
||||
|
||||
/// Returns a date representing the last day of the week.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the last day of the week happens to fall just out of range of `NaiveDate`
|
||||
/// (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -113,6 +101,11 @@ impl NaiveWeek {
|
||||
/// [first_day](./struct.NaiveWeek.html#method.first_day) and
|
||||
/// [last_day](./struct.NaiveWeek.html#method.last_day) functions.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the either the first or last day of the week happens to fall just out of range of
|
||||
/// `NaiveDate` (more than ca. 262,000 years away from common era).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@ -136,7 +129,7 @@ impl NaiveWeek {
|
||||
/// that adding `TimeDelta::days(1)` doesn't increment the day value as expected due to it being a
|
||||
/// fixed number of seconds. This difference applies only when dealing with `DateTime<TimeZone>` data types
|
||||
/// and in other cases `TimeDelta::days(n)` and `Days::new(n)` are equivalent.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Days(pub(crate) u64);
|
||||
|
||||
impl Days {
|
||||
@ -218,34 +211,6 @@ impl arbitrary::Arbitrary<'_> for NaiveDate {
|
||||
}
|
||||
}
|
||||
|
||||
// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`,
|
||||
// we use a separate run-time test.
|
||||
#[test]
|
||||
fn test_date_bounds() {
|
||||
let calculated_min = NaiveDate::from_ymd_opt(MIN_YEAR, 1, 1).unwrap();
|
||||
let calculated_max = NaiveDate::from_ymd_opt(MAX_YEAR, 12, 31).unwrap();
|
||||
assert!(
|
||||
NaiveDate::MIN == calculated_min,
|
||||
"`NaiveDate::MIN` should have a year flag {:?}",
|
||||
calculated_min.of().flags()
|
||||
);
|
||||
assert!(
|
||||
NaiveDate::MAX == calculated_max,
|
||||
"`NaiveDate::MAX` should have a year flag {:?}",
|
||||
calculated_max.of().flags()
|
||||
);
|
||||
|
||||
// let's also check that the entire range do not exceed 2^44 seconds
|
||||
// (sometimes used for bounding `TimeDelta` against overflow)
|
||||
let maxsecs = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds();
|
||||
let maxsecs = maxsecs + 86401; // also take care of DateTime
|
||||
assert!(
|
||||
maxsecs < (1 << MAX_BITS),
|
||||
"The entire `NaiveDate` range somehow exceeds 2^{} seconds",
|
||||
MAX_BITS
|
||||
);
|
||||
}
|
||||
|
||||
impl NaiveDate {
|
||||
pub(crate) fn weeks_from(&self, day: Weekday) -> i32 {
|
||||
(self.ordinal() as i32 - self.weekday().num_days_from(day) as i32 + 6) / 7
|
||||
@ -284,7 +249,10 @@ impl NaiveDate {
|
||||
/// Makes a new `NaiveDate` from the [calendar date](#calendar-date)
|
||||
/// (year, month and day).
|
||||
///
|
||||
/// Panics on the out-of-range date, invalid month and/or day.
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the specified calendar day does not exist, on invalid values for `month` or `day`,
|
||||
/// or if `year` is out of range for `NaiveDate`.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_ymd_opt()` instead")]
|
||||
#[must_use]
|
||||
pub fn from_ymd(year: i32, month: u32, day: u32) -> NaiveDate {
|
||||
@ -294,7 +262,12 @@ impl NaiveDate {
|
||||
/// Makes a new `NaiveDate` from the [calendar date](#calendar-date)
|
||||
/// (year, month and day).
|
||||
///
|
||||
/// Returns `None` on the out-of-range date, invalid month and/or day.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The specified calendar day does not exist (for example 2023-04-31).
|
||||
/// - The value for `month` or `day` is invalid.
|
||||
/// - `year` is out of range for `NaiveDate`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -319,7 +292,10 @@ impl NaiveDate {
|
||||
/// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date)
|
||||
/// (year and day of the year).
|
||||
///
|
||||
/// Panics on the out-of-range date and/or invalid day of year.
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the specified ordinal day does not exist, on invalid values for `ordinal`, or if
|
||||
/// `year` is out of range for `NaiveDate`.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_yo_opt()` instead")]
|
||||
#[must_use]
|
||||
pub fn from_yo(year: i32, ordinal: u32) -> NaiveDate {
|
||||
@ -329,7 +305,12 @@ impl NaiveDate {
|
||||
/// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date)
|
||||
/// (year and day of the year).
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid day of year.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The specified ordinal day does not exist (for example 2023-366).
|
||||
/// - The value for `ordinal` is invalid (for example: `0`, `400`).
|
||||
/// - `year` is out of range for `NaiveDate`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -356,7 +337,10 @@ impl NaiveDate {
|
||||
/// (year, week number and day of the week).
|
||||
/// The resulting `NaiveDate` may have a different year from the input year.
|
||||
///
|
||||
/// Panics on the out-of-range date and/or invalid week number.
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the specified week does not exist in that year, on invalid values for `week`, or
|
||||
/// if the resulting date is out of range for `NaiveDate`.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_isoywd_opt()` instead")]
|
||||
#[must_use]
|
||||
pub fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> NaiveDate {
|
||||
@ -367,7 +351,12 @@ impl NaiveDate {
|
||||
/// (year, week number and day of the week).
|
||||
/// The resulting `NaiveDate` may have a different year from the input year.
|
||||
///
|
||||
/// Returns `None` on the out-of-range date and/or invalid week number.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The specified week does not exist in that year (for example 2023 week 53).
|
||||
/// - The value for `week` is invalid (for example: `0`, `60`).
|
||||
/// - If the resulting date is out of range for `NaiveDate`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -443,6 +432,8 @@ impl NaiveDate {
|
||||
/// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
|
||||
/// January 1, 1 being day 1.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the date is out of range.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_num_days_from_ce_opt()` instead")]
|
||||
#[inline]
|
||||
@ -454,6 +445,8 @@ impl NaiveDate {
|
||||
/// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with
|
||||
/// January 1, 1 being day 1.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the date is out of range.
|
||||
///
|
||||
/// # Example
|
||||
@ -481,15 +474,15 @@ impl NaiveDate {
|
||||
}
|
||||
|
||||
/// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
|
||||
/// since the beginning of the given month. For instance, if you want the 2nd Friday of March
|
||||
/// since the beginning of the given month. For instance, if you want the 2nd Friday of March
|
||||
/// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`.
|
||||
///
|
||||
/// `n` is 1-indexed.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The resulting `NaiveDate` is guaranteed to be in `month`. If `n` is larger than the number
|
||||
/// of `weekday` in `month` (eg. the 6th Friday of March 2017) then this function will panic.
|
||||
///
|
||||
/// `n` is 1-indexed. Passing `n=0` will cause a panic.
|
||||
/// Panics if the specified day does not exist in that month, on invalid values for `month` or
|
||||
/// `n`, or if `year` is out of range for `NaiveDate`.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_weekday_of_month_opt()` instead")]
|
||||
#[must_use]
|
||||
pub fn from_weekday_of_month(year: i32, month: u32, weekday: Weekday, n: u8) -> NaiveDate {
|
||||
@ -497,17 +490,25 @@ impl NaiveDate {
|
||||
}
|
||||
|
||||
/// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
|
||||
/// since the beginning of the given month. For instance, if you want the 2nd Friday of March
|
||||
/// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`. `n` is 1-indexed.
|
||||
/// since the beginning of the given month. For instance, if you want the 2nd Friday of March
|
||||
/// 2017, you would use `NaiveDate::from_weekday_of_month(2017, 3, Weekday::Fri, 2)`.
|
||||
///
|
||||
/// `n` is 1-indexed.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if:
|
||||
/// - The specified day does not exist in that month (for example the 5th Monday of Apr. 2023).
|
||||
/// - The value for `month` or `n` is invalid.
|
||||
/// - `year` is out of range for `NaiveDate`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use chrono::{NaiveDate, Weekday};
|
||||
/// assert_eq!(NaiveDate::from_weekday_of_month_opt(2017, 3, Weekday::Fri, 2),
|
||||
/// NaiveDate::from_ymd_opt(2017, 3, 10))
|
||||
/// ```
|
||||
///
|
||||
/// Returns `None` if `n` out-of-range; ie. if `n` is larger than the number of `weekday` in
|
||||
/// `month` (eg. the 6th Friday of March 2017), or if `n == 0`.
|
||||
#[must_use]
|
||||
pub fn from_weekday_of_month_opt(
|
||||
year: i32,
|
||||
@ -596,10 +597,14 @@ impl NaiveDate {
|
||||
|
||||
/// Add a duration in [`Months`] to the date
|
||||
///
|
||||
/// If the day would be out of range for the resulting month, use the last day for that month.
|
||||
/// Uses the last day of the month if the day does not exist in the resulting month.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use chrono::{NaiveDate, Months};
|
||||
/// assert_eq!(
|
||||
@ -625,10 +630,14 @@ impl NaiveDate {
|
||||
|
||||
/// Subtract a duration in [`Months`] from the date
|
||||
///
|
||||
/// If the day would be out of range for the resulting month, use the last day for that month.
|
||||
/// Uses the last day of the month if the day does not exist in the resulting month.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use chrono::{NaiveDate, Months};
|
||||
/// assert_eq!(
|
||||
@ -699,8 +708,12 @@ impl NaiveDate {
|
||||
|
||||
/// Add a duration in [`Days`] to the date
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use chrono::{NaiveDate, Days};
|
||||
/// assert_eq!(
|
||||
@ -727,8 +740,12 @@ impl NaiveDate {
|
||||
|
||||
/// Subtract a duration in [`Days`] from the date
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use chrono::{NaiveDate, Days};
|
||||
/// assert_eq!(
|
||||
@ -782,6 +799,8 @@ impl NaiveDate {
|
||||
/// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here;
|
||||
/// use `NaiveDate::and_hms_*` methods with a subsecond parameter instead.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute and/or second.
|
||||
#[deprecated(since = "0.4.23", note = "use `and_hms_opt()` instead")]
|
||||
#[inline]
|
||||
@ -795,6 +814,8 @@ impl NaiveDate {
|
||||
/// No [leap second](./struct.NaiveTime.html#leap-second-handling) is allowed here;
|
||||
/// use `NaiveDate::and_hms_*_opt` methods with a subsecond parameter instead.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute and/or second.
|
||||
///
|
||||
/// # Example
|
||||
@ -819,6 +840,8 @@ impl NaiveDate {
|
||||
/// The millisecond part can exceed 1,000
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or millisecond.
|
||||
#[deprecated(since = "0.4.23", note = "use `and_hms_milli_opt()` instead")]
|
||||
#[inline]
|
||||
@ -832,6 +855,8 @@ impl NaiveDate {
|
||||
/// The millisecond part can exceed 1,000
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -864,6 +889,8 @@ impl NaiveDate {
|
||||
/// The microsecond part can exceed 1,000,000
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or microsecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -891,6 +918,8 @@ impl NaiveDate {
|
||||
/// The microsecond part can exceed 1,000,000
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or microsecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -923,6 +952,8 @@ impl NaiveDate {
|
||||
/// The nanosecond part can exceed 1,000,000,000
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or nanosecond.
|
||||
#[deprecated(since = "0.4.23", note = "use `and_hms_nano_opt()` instead")]
|
||||
#[inline]
|
||||
@ -936,6 +967,8 @@ impl NaiveDate {
|
||||
/// The nanosecond part can exceed 1,000,000,000
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -994,6 +1027,8 @@ impl NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` for the next calendar date.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when `self` is the last representable date.
|
||||
#[deprecated(since = "0.4.23", note = "use `succ_opt()` instead")]
|
||||
#[inline]
|
||||
@ -1004,6 +1039,8 @@ impl NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` for the next calendar date.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when `self` is the last representable date.
|
||||
///
|
||||
/// # Example
|
||||
@ -1026,6 +1063,8 @@ impl NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` for the previous calendar date.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when `self` is the first representable date.
|
||||
#[deprecated(since = "0.4.23", note = "use `pred_opt()` instead")]
|
||||
#[inline]
|
||||
@ -1036,6 +1075,8 @@ impl NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` for the previous calendar date.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` when `self` is the first representable date.
|
||||
///
|
||||
/// # Example
|
||||
@ -1056,9 +1097,11 @@ impl NaiveDate {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the `days` part of given `Duration` to the current date.
|
||||
/// Adds the number of whole days in the given `Duration` to the current date.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1088,9 +1131,11 @@ impl NaiveDate {
|
||||
NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags)
|
||||
}
|
||||
|
||||
/// Subtracts the `days` part of given `TimeDelta` from the current date.
|
||||
/// Subtracts the number of whole days in the given `TimeDelta` from the current date.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1156,6 +1201,10 @@ impl NaiveDate {
|
||||
}
|
||||
|
||||
/// Returns the number of whole years from the given `base` until `self`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `base < self`.
|
||||
#[must_use]
|
||||
pub fn years_since(&self, base: Self) -> Option<u32> {
|
||||
let mut years = self.year() - base.year();
|
||||
@ -1539,9 +1588,12 @@ impl Datelike for NaiveDate {
|
||||
isoweek::iso_week_from_yof(self.year(), self.of())
|
||||
}
|
||||
|
||||
/// Makes a new `NaiveDate` with the year number changed.
|
||||
/// Makes a new `NaiveDate` with the year number changed, while keeping the same month and day.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or when the `NaiveDate` would be
|
||||
/// out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1575,7 +1627,9 @@ impl Datelike for NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` with the month number (starting from 1) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `month` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1594,7 +1648,10 @@ impl Datelike for NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `month0` is
|
||||
/// invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1614,7 +1671,9 @@ impl Datelike for NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` with the day of month (starting from 1) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `day` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1633,7 +1692,9 @@ impl Datelike for NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` with the day of month (starting from 0) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `day0` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1653,7 +1714,10 @@ impl Datelike for NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` with the day of year (starting from 1) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `ordinal` is
|
||||
/// invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1677,7 +1741,10 @@ impl Datelike for NaiveDate {
|
||||
|
||||
/// Makes a new `NaiveDate` with the day of year (starting from 0) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDate` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `ordinal0` is
|
||||
/// invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1898,13 +1965,10 @@ impl Iterator for NaiveDateDaysIterator {
|
||||
type Item = NaiveDate;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.value == NaiveDate::MAX {
|
||||
return None;
|
||||
}
|
||||
// current < NaiveDate::MAX from here on:
|
||||
// We return the current value, and have no way to return `NaiveDate::MAX`.
|
||||
let current = self.value;
|
||||
// This can't panic because current is < NaiveDate::MAX:
|
||||
self.value = current.succ_opt().unwrap();
|
||||
self.value = current.succ_opt()?;
|
||||
Some(current)
|
||||
}
|
||||
|
||||
@ -1918,15 +1982,16 @@ impl ExactSizeIterator for NaiveDateDaysIterator {}
|
||||
|
||||
impl DoubleEndedIterator for NaiveDateDaysIterator {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.value == NaiveDate::MIN {
|
||||
return None;
|
||||
}
|
||||
// We return the current value, and have no way to return `NaiveDate::MIN`.
|
||||
let current = self.value;
|
||||
self.value = current.pred_opt().unwrap();
|
||||
self.value = current.pred_opt()?;
|
||||
Some(current)
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for NaiveDateDaysIterator {}
|
||||
|
||||
/// Iterator over `NaiveDate` with a step size of one week.
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct NaiveDateWeeksIterator {
|
||||
value: NaiveDate,
|
||||
@ -1936,11 +2001,8 @@ impl Iterator for NaiveDateWeeksIterator {
|
||||
type Item = NaiveDate;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if NaiveDate::MAX - self.value < TimeDelta::weeks(1) {
|
||||
return None;
|
||||
}
|
||||
let current = self.value;
|
||||
self.value = current + TimeDelta::weeks(1);
|
||||
self.value = current.checked_add_signed(TimeDelta::weeks(1))?;
|
||||
Some(current)
|
||||
}
|
||||
|
||||
@ -1954,18 +2016,13 @@ impl ExactSizeIterator for NaiveDateWeeksIterator {}
|
||||
|
||||
impl DoubleEndedIterator for NaiveDateWeeksIterator {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.value - NaiveDate::MIN < TimeDelta::weeks(1) {
|
||||
return None;
|
||||
}
|
||||
let current = self.value;
|
||||
self.value = current - TimeDelta::weeks(1);
|
||||
self.value = current.checked_sub_signed(TimeDelta::weeks(1))?;
|
||||
Some(current)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: NaiveDateDaysIterator and NaiveDateWeeksIterator should implement FusedIterator,
|
||||
// TrustedLen, and Step once they becomes stable.
|
||||
// See: https://github.com/chronotope/chrono/issues/208
|
||||
impl FusedIterator for NaiveDateWeeksIterator {}
|
||||
|
||||
/// The `Debug` output of the naive date `d` is the same as
|
||||
/// [`d.format("%Y-%m-%d")`](../format/strftime/index.html).
|
||||
@ -2219,38 +2276,70 @@ mod serde {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
super::test_encodable_json(serde_json::to_string);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::naive::date::{test_decodable_json, test_encodable_json};
|
||||
use crate::NaiveDate;
|
||||
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
super::test_decodable_json(|input| serde_json::from_str(input));
|
||||
}
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
test_encodable_json(serde_json::to_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
test_decodable_json(|input| serde_json::from_str(input));
|
||||
}
|
||||
|
||||
let d = NaiveDate::from_ymd_opt(2014, 7, 24).unwrap();
|
||||
let encoded = serialize(&d).unwrap();
|
||||
let decoded: NaiveDate = deserialize(&encoded).unwrap();
|
||||
assert_eq!(d, decoded);
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
let d = NaiveDate::from_ymd_opt(2014, 7, 24).unwrap();
|
||||
let encoded = serialize(&d).unwrap();
|
||||
let decoded: NaiveDate = deserialize(&encoded).unwrap();
|
||||
assert_eq!(d, decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
Days, Months, NaiveDate, MAX_DAYS_FROM_YEAR_0, MAX_YEAR, MIN_DAYS_FROM_YEAR_0, MIN_YEAR,
|
||||
};
|
||||
use super::{Days, Months, NaiveDate, MAX_YEAR, MIN_YEAR};
|
||||
use crate::time_delta::TimeDelta;
|
||||
use crate::{Datelike, Weekday};
|
||||
use std::{i32, u32};
|
||||
|
||||
// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`,
|
||||
// we use a separate run-time test.
|
||||
#[test]
|
||||
fn test_date_bounds() {
|
||||
let calculated_min = NaiveDate::from_ymd_opt(MIN_YEAR, 1, 1).unwrap();
|
||||
let calculated_max = NaiveDate::from_ymd_opt(MAX_YEAR, 12, 31).unwrap();
|
||||
assert!(
|
||||
NaiveDate::MIN == calculated_min,
|
||||
"`NaiveDate::MIN` should have a year flag {:?}",
|
||||
calculated_min.of().flags()
|
||||
);
|
||||
assert!(
|
||||
NaiveDate::MAX == calculated_max,
|
||||
"`NaiveDate::MAX` should have a year flag {:?}",
|
||||
calculated_max.of().flags()
|
||||
);
|
||||
|
||||
// let's also check that the entire range do not exceed 2^44 seconds
|
||||
// (sometimes used for bounding `Duration` against overflow)
|
||||
let maxsecs = NaiveDate::MAX.signed_duration_since(NaiveDate::MIN).num_seconds();
|
||||
let maxsecs = maxsecs + 86401; // also take care of DateTime
|
||||
assert!(
|
||||
maxsecs < (1 << MAX_BITS),
|
||||
"The entire `NaiveDate` range somehow exceeds 2^{} seconds",
|
||||
MAX_BITS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_months() {
|
||||
// identity
|
||||
@ -3102,4 +3191,25 @@ mod tests {
|
||||
assert!(dt.with_day0(4294967295).is_none());
|
||||
assert!(dt.with_ordinal0(4294967295).is_none());
|
||||
}
|
||||
|
||||
// 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
|
||||
// = MAX_YEAR * 365 + (# of leap years from 0001 to MAX_YEAR) + 365 days
|
||||
const MAX_DAYS_FROM_YEAR_0: i32 =
|
||||
MAX_YEAR * 365 + MAX_YEAR / 4 - MAX_YEAR / 100 + MAX_YEAR / 400 + 365;
|
||||
|
||||
// MIN_YEAR-01-01 minus 0000-01-01
|
||||
// = (MIN_YEAR+400n+1)-01-01 minus (400n+1)-01-01
|
||||
// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - ((400n+1)-01-01 minus 0001-01-01)
|
||||
// = ((MIN_YEAR+400n+1)-01-01 minus 0001-01-01) - 146097n days
|
||||
//
|
||||
// n is set to 1000 for convenience.
|
||||
const MIN_DAYS_FROM_YEAR_0: i32 = (MIN_YEAR + 400_000) * 365 + (MIN_YEAR + 400_000) / 4
|
||||
- (MIN_YEAR + 400_000) / 100
|
||||
+ (MIN_YEAR + 400_000) / 400
|
||||
- 146_097_000;
|
||||
|
||||
// only used for testing, but duplicated in naive::datetime
|
||||
const MAX_BITS: usize = 44;
|
||||
}
|
||||
|
@ -109,7 +109,11 @@ impl NaiveDateTime {
|
||||
/// [leap second](./struct.NaiveTime.html#leap-second-handling). (The true "UNIX
|
||||
/// timestamp" cannot represent a leap second unambiguously.)
|
||||
///
|
||||
/// Panics on the out-of-range number of seconds and/or invalid nanosecond.
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of seconds would be out of range for a `NaiveDateTime` (more than
|
||||
/// ca. 262,000 years away from common era), and panics on an invalid nanosecond (2 seconds or
|
||||
/// more).
|
||||
#[deprecated(since = "0.4.23", note = "use `from_timestamp_opt()` instead")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
@ -122,7 +126,10 @@ impl NaiveDateTime {
|
||||
///
|
||||
/// The UNIX epoch starts on midnight, January 1, 1970, UTC.
|
||||
///
|
||||
/// Returns `None` on an out-of-range number of milliseconds.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the number of milliseconds would be out of range for a `NaiveDateTime`
|
||||
/// (more than ca. 262,000 years away from common era)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -151,7 +158,10 @@ impl NaiveDateTime {
|
||||
///
|
||||
/// The UNIX epoch starts on midnight, January 1, 1970, UTC.
|
||||
///
|
||||
/// Returns `None` on an out-of-range number of microseconds.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the number of microseconds would be out of range for a `NaiveDateTime`
|
||||
/// (more than ca. 262,000 years away from common era)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -185,8 +195,11 @@ impl NaiveDateTime {
|
||||
/// in order to represent the [leap second](./struct.NaiveTime.html#leap-second-handling).
|
||||
/// (The true "UNIX timestamp" cannot represent a leap second unambiguously.)
|
||||
///
|
||||
/// Returns `None` on the out-of-range number of seconds (more than 262 000 years away
|
||||
/// from common era) and/or invalid nanosecond (2 seconds or more).
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the number of seconds would be out of range for a `NaiveDateTime` (more
|
||||
/// than ca. 262,000 years away from common era), and panics on an invalid nanosecond
|
||||
/// (2 seconds or more).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -386,11 +399,6 @@ impl NaiveDateTime {
|
||||
/// Note that this does *not* account for the timezone!
|
||||
/// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch.
|
||||
///
|
||||
/// Note also that this does reduce the number of years that can be
|
||||
/// represented from ~584 Billion to ~584 Million. (If this is a problem,
|
||||
/// please file an issue to let me know what domain needs millisecond
|
||||
/// precision over billions of years, I'm curious.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -417,11 +425,6 @@ impl NaiveDateTime {
|
||||
/// Note that this does *not* account for the timezone!
|
||||
/// The true "UNIX timestamp" would count seconds since the midnight *UTC* on the epoch.
|
||||
///
|
||||
/// Note also that this does reduce the number of years that can be
|
||||
/// represented from ~584 Billion to ~584 Thousand. (If this is a problem,
|
||||
/// please file an issue to let me know what domain needs microsecond
|
||||
/// precision over millennia, I'm curious.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -447,13 +450,11 @@ impl NaiveDateTime {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Note also that this does reduce the number of years that can be
|
||||
/// represented from ~584 Billion to ~584 years. The dates that can be
|
||||
/// represented as nanoseconds are between 1677-09-21T00:12:44.0 and
|
||||
/// 2262-04-11T23:47:16.854775804.
|
||||
/// An `i64` with nanosecond precision can span a range of ~584 years. This function panics on
|
||||
/// an out of range `NaiveDateTime`.
|
||||
///
|
||||
/// (If this is a problem, please file an issue to let me know what domain
|
||||
/// needs nanosecond precision over millennia, I'm curious.)
|
||||
/// The dates that can be represented as nanoseconds are between 1677-09-21T00:12:44.0 and
|
||||
/// 2262-04-11T23:47:16.854775804.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -476,8 +477,10 @@ impl NaiveDateTime {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn timestamp_nanos(&self) -> i64 {
|
||||
let as_ns = self.timestamp() * 1_000_000_000;
|
||||
as_ns + i64::from(self.timestamp_subsec_nanos())
|
||||
self.timestamp()
|
||||
.checked_mul(1_000_000_000)
|
||||
.and_then(|ns| ns.checked_add(i64::from(self.timestamp_subsec_nanos())))
|
||||
.expect("value can not be represented in a timestamp with nanosecond precision.")
|
||||
}
|
||||
|
||||
/// Returns the number of milliseconds since the last whole non-leap second.
|
||||
@ -553,7 +556,9 @@ impl NaiveDateTime {
|
||||
/// except when the `NaiveDateTime` itself represents a leap second
|
||||
/// in which case the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -626,9 +631,11 @@ impl NaiveDateTime {
|
||||
|
||||
/// Adds given `Months` to the current date and time.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// Uses the last day of the month if the day does not exist in the resulting month.
|
||||
///
|
||||
/// Overflow returns `None`.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -659,7 +666,9 @@ impl NaiveDateTime {
|
||||
/// except when the `NaiveDateTime` itself represents a leap second
|
||||
/// in which case the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -728,9 +737,11 @@ impl NaiveDateTime {
|
||||
|
||||
/// Subtracts given `Months` from the current date and time.
|
||||
///
|
||||
/// Returns `None` when it will result in overflow.
|
||||
/// Uses the last day of the month if the day does not exist in the resulting month.
|
||||
///
|
||||
/// Overflow returns `None`.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date would be out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1091,12 +1102,16 @@ impl Datelike for NaiveDateTime {
|
||||
self.date.iso_week()
|
||||
}
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the year number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
/// Makes a new `NaiveDateTime` with the year number changed, while keeping the same month and
|
||||
/// day.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_year`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or when the `NaiveDateTime` would be
|
||||
/// out of range.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1113,10 +1128,12 @@ impl Datelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the month number (starting from 1) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_month`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `month` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1134,10 +1151,13 @@ impl Datelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the month number (starting from 0) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_month0`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `month0` is
|
||||
/// invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1155,10 +1175,12 @@ impl Datelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the day of month (starting from 1) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_day`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `day` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1175,10 +1197,12 @@ impl Datelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the day of month (starting from 0) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_day0`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `day0` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1195,10 +1219,13 @@ impl Datelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the day of year (starting from 1) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_ordinal`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `ordinal` is
|
||||
/// invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1222,10 +1249,13 @@ impl Datelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the day of year (starting from 0) changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveDate::with_ordinal0`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the resulting date does not exist, or if the value for `ordinal0` is
|
||||
/// invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1321,10 +1351,12 @@ impl Timelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the hour number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
///
|
||||
/// See also the [`NaiveTime::with_hour`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the value for `hour` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1342,10 +1374,11 @@ impl Timelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the minute number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid.
|
||||
/// See also the [`NaiveTime::with_minute`] method.
|
||||
///
|
||||
/// See also the
|
||||
/// [`NaiveTime::with_minute`] method.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the value for `minute` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -1364,12 +1397,15 @@ impl Timelike for NaiveDateTime {
|
||||
|
||||
/// Makes a new `NaiveDateTime` with the second number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveDateTime` would be invalid. As
|
||||
/// with the [`NaiveDateTime::second`] method, the input range is
|
||||
/// restricted to 0 through 59.
|
||||
/// As with the [`second`](#method.second) method,
|
||||
/// the input range is restricted to 0 through 59.
|
||||
///
|
||||
/// See also the [`NaiveTime::with_second`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the value for `second` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1393,6 +1429,10 @@ impl Timelike for NaiveDateTime {
|
||||
///
|
||||
/// See also the [`NaiveTime::with_nanosecond`] method.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `nanosecond >= 2,000,000,000`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -1417,7 +1457,9 @@ impl Timelike for NaiveDateTime {
|
||||
/// second ever**, except when the `NaiveDateTime` itself represents a leap second in which case
|
||||
/// the assumption becomes that **there is exactly a single leap second ever**.
|
||||
///
|
||||
/// Panics on underflow or overflow. Use [`NaiveDateTime::checked_add_signed`]
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the resulting date would be out of range. Use [`NaiveDateTime::checked_add_signed`]
|
||||
/// to detect that.
|
||||
///
|
||||
/// # Example
|
||||
|
@ -110,7 +110,6 @@ pub mod ts_nanoseconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -138,7 +137,6 @@ pub mod ts_nanoseconds {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355733).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -233,7 +231,6 @@ pub mod ts_nanoseconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -264,7 +261,6 @@ pub mod ts_nanoseconds_option {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355733) });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -362,7 +358,6 @@ pub mod ts_microseconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -390,7 +385,6 @@ pub mod ts_microseconds {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355000).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -488,7 +482,6 @@ pub mod ts_microseconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -519,7 +512,6 @@ pub mod ts_microseconds_option {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918355000) });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -617,7 +609,6 @@ pub mod ts_milliseconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -645,7 +636,6 @@ pub mod ts_milliseconds {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918000000).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -740,7 +730,6 @@ pub mod ts_milliseconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -771,7 +760,6 @@ pub mod ts_milliseconds_option {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1526522699, 918000000) });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -869,7 +857,6 @@ pub mod ts_seconds {
|
||||
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -897,7 +884,6 @@ pub mod ts_seconds {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1431684000, 0).unwrap() });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -989,7 +975,6 @@ pub mod ts_seconds_option {
|
||||
/// assert_eq!(as_string, r#"{"time":1526522699}"#);
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
@ -1020,7 +1005,6 @@ pub mod ts_seconds_option {
|
||||
/// assert_eq!(my_s, S { time: NaiveDateTime::from_timestamp_opt(1431684000, 0) });
|
||||
/// # Ok::<(), serde_json::Error>(())
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
@ -1063,51 +1047,6 @@ pub mod ts_seconds_option {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
super::test_encodable_json(serde_json::to_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
super::test_decodable_json(|input| serde_json::from_str(input));
|
||||
}
|
||||
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
use crate::NaiveDate;
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap();
|
||||
let encoded = serialize(&dt).unwrap();
|
||||
let decoded: NaiveDateTime = deserialize(&encoded).unwrap();
|
||||
assert_eq!(dt, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_bincode_optional() {
|
||||
use crate::prelude::*;
|
||||
use crate::serde::ts_nanoseconds_option;
|
||||
use bincode::{deserialize, serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Test {
|
||||
one: Option<i64>,
|
||||
#[serde(with = "ts_nanoseconds_option")]
|
||||
two: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
let expected =
|
||||
Test { one: Some(1), two: Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap()) };
|
||||
let bytes: Vec<u8> = serialize(&expected).unwrap();
|
||||
let actual = deserialize::<Test>(&(bytes)).unwrap();
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
// lik? function to convert a LocalResult into a serde-ish Result
|
||||
pub(crate) fn serde_from<T, E, V>(me: LocalResult<T>, ts: &V) -> Result<T, E>
|
||||
where
|
||||
@ -1155,3 +1094,51 @@ impl<V: fmt::Display, D: fmt::Display> fmt::Display for SerdeError<V, D> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::naive::datetime::{test_decodable_json, test_encodable_json};
|
||||
use crate::serde::ts_nanoseconds_option;
|
||||
use crate::{DateTime, NaiveDate, NaiveDateTime, TimeZone, Utc};
|
||||
|
||||
use bincode::{deserialize, serialize};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
test_encodable_json(serde_json::to_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
test_decodable_json(|input| serde_json::from_str(input));
|
||||
}
|
||||
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
let dt =
|
||||
NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap();
|
||||
let encoded = serialize(&dt).unwrap();
|
||||
let decoded: NaiveDateTime = deserialize(&encoded).unwrap();
|
||||
assert_eq!(dt, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_bincode_optional() {
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Test {
|
||||
one: Option<i64>,
|
||||
#[serde(with = "ts_nanoseconds_option")]
|
||||
two: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
let expected =
|
||||
Test { one: Some(1), two: Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap()) };
|
||||
let bytes: Vec<u8> = serialize(&expected).unwrap();
|
||||
let actual = deserialize::<Test>(&(bytes)).unwrap();
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
||||
|
@ -369,6 +369,24 @@ fn test_nanosecond_range() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_nanosecond_just_beyond_range() {
|
||||
let maximum = "2262-04-11T23:47:16.854775804";
|
||||
let parsed: NaiveDateTime = maximum.parse().unwrap();
|
||||
let beyond_max = parsed + TimeDelta::milliseconds(300);
|
||||
let _ = beyond_max.timestamp_nanos();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_nanosecond_far_beyond_range() {
|
||||
let maximum = "2262-04-11T23:47:16.854775804";
|
||||
let parsed: NaiveDateTime = maximum.parse().unwrap();
|
||||
let beyond_max = parsed + TimeDelta::days(365);
|
||||
let _ = beyond_max.timestamp_nanos();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_local_timezone() {
|
||||
let ndt = NaiveDate::from_ymd_opt(2022, 6, 15).unwrap().and_hms_opt(18, 59, 36).unwrap();
|
||||
|
@ -10,8 +10,9 @@ mod internals;
|
||||
mod isoweek;
|
||||
mod time;
|
||||
|
||||
pub use self::date::{Days, NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator, NaiveWeek};
|
||||
#[allow(deprecated)]
|
||||
pub use self::date::{Days, NaiveDate, NaiveWeek, MAX_DATE, MIN_DATE};
|
||||
pub use self::date::{MAX_DATE, MIN_DATE};
|
||||
#[allow(deprecated)]
|
||||
pub use self::datetime::{NaiveDateTime, MAX_DATETIME, MIN_DATETIME};
|
||||
pub use self::isoweek::IsoWeek;
|
||||
|
@ -208,6 +208,8 @@ impl NaiveTime {
|
||||
/// No [leap second](#leap-second-handling) is allowed here;
|
||||
/// use `NaiveTime::from_hms_*` methods with a subsecond parameter instead.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute and/or second.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_hms_opt()` instead")]
|
||||
#[inline]
|
||||
@ -221,6 +223,8 @@ impl NaiveTime {
|
||||
/// No [leap second](#leap-second-handling) is allowed here;
|
||||
/// use `NaiveTime::from_hms_*_opt` methods with a subsecond parameter instead.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute and/or second.
|
||||
///
|
||||
/// # Example
|
||||
@ -247,6 +251,8 @@ impl NaiveTime {
|
||||
/// The millisecond part can exceed 1,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or millisecond.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_hms_milli_opt()` instead")]
|
||||
#[inline]
|
||||
@ -260,6 +266,8 @@ impl NaiveTime {
|
||||
/// The millisecond part can exceed 1,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or millisecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -290,6 +298,8 @@ impl NaiveTime {
|
||||
/// The microsecond part can exceed 1,000,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or microsecond.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_hms_micro_opt()` instead")]
|
||||
#[inline]
|
||||
@ -303,6 +313,8 @@ impl NaiveTime {
|
||||
/// The microsecond part can exceed 1,000,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or microsecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -331,6 +343,8 @@ impl NaiveTime {
|
||||
/// The nanosecond part can exceed 1,000,000,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid hour, minute, second and/or nanosecond.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_hms_nano_opt()` instead")]
|
||||
#[inline]
|
||||
@ -344,6 +358,8 @@ impl NaiveTime {
|
||||
/// The nanosecond part can exceed 1,000,000,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -376,6 +392,8 @@ impl NaiveTime {
|
||||
/// The nanosecond part can exceed 1,000,000,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics on invalid number of seconds and/or nanosecond.
|
||||
#[deprecated(since = "0.4.23", note = "use `from_num_seconds_from_midnight_opt()` instead")]
|
||||
#[inline]
|
||||
@ -389,6 +407,8 @@ impl NaiveTime {
|
||||
/// The nanosecond part can exceed 1,000,000,000
|
||||
/// in order to represent the [leap second](#leap-second-handling).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` on invalid number of seconds and/or nanosecond.
|
||||
///
|
||||
/// # Example
|
||||
@ -502,10 +522,8 @@ impl NaiveTime {
|
||||
parsed.to_naive_time().map(|t| (t, remainder))
|
||||
}
|
||||
|
||||
/// Adds given `TimeDelta` to the current time,
|
||||
/// and also returns the number of *seconds*
|
||||
/// Adds given `TimeDelta` to the current time, and also returns the number of *seconds*
|
||||
/// in the integral number of days ignored from the addition.
|
||||
/// (We cannot return `TimeDelta` because it is subject to overflow or underflow.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -585,10 +603,8 @@ impl NaiveTime {
|
||||
(NaiveTime { secs: secs as u32, frac: frac as u32 }, morerhssecs)
|
||||
}
|
||||
|
||||
/// Subtracts given `TimeDelta` from the current time,
|
||||
/// and also returns the number of *seconds*
|
||||
/// Subtracts given `TimeDelta` from the current time, and also returns the number of *seconds*
|
||||
/// in the integral number of days ignored from the subtraction.
|
||||
/// (We cannot return `TimeDelta` because it is subject to overflow or underflow.)
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -882,7 +898,9 @@ impl Timelike for NaiveTime {
|
||||
|
||||
/// Makes a new `NaiveTime` with the hour number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveTime` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the value for `hour` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -904,7 +922,9 @@ impl Timelike for NaiveTime {
|
||||
|
||||
/// Makes a new `NaiveTime` with the minute number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveTime` would be invalid.
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the value for `minute` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -926,10 +946,13 @@ impl Timelike for NaiveTime {
|
||||
|
||||
/// Makes a new `NaiveTime` with the second number changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveTime` would be invalid.
|
||||
/// As with the [`second`](#method.second) method,
|
||||
/// the input range is restricted to 0 through 59.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if the value for `second` is invalid.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
@ -950,10 +973,13 @@ impl Timelike for NaiveTime {
|
||||
|
||||
/// Makes a new `NaiveTime` with nanoseconds since the whole non-leap second changed.
|
||||
///
|
||||
/// Returns `None` when the resulting `NaiveTime` would be invalid.
|
||||
/// As with the [`nanosecond`](#method.nanosecond) method,
|
||||
/// the input range can exceed 1,000,000,000 for leap seconds.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if `nanosecond >= 2,000,000,000`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
|
@ -42,24 +42,30 @@ impl<'de> de::Deserialize<'de> for NaiveTime {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
super::test_encodable_json(serde_json::to_string);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::naive::time::{test_decodable_json, test_encodable_json};
|
||||
use crate::NaiveTime;
|
||||
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
super::test_decodable_json(|input| serde_json::from_str(input));
|
||||
}
|
||||
#[test]
|
||||
fn test_serde_serialize() {
|
||||
test_encodable_json(serde_json::to_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
#[test]
|
||||
fn test_serde_deserialize() {
|
||||
test_decodable_json(|input| serde_json::from_str(input));
|
||||
}
|
||||
|
||||
let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap();
|
||||
let encoded = serialize(&t).unwrap();
|
||||
let decoded: NaiveTime = deserialize(&encoded).unwrap();
|
||||
assert_eq!(t, decoded);
|
||||
#[test]
|
||||
fn test_serde_bincode() {
|
||||
// Bincode is relevant to test separately from JSON because
|
||||
// it is not self-describing.
|
||||
use bincode::{deserialize, serialize};
|
||||
|
||||
let t = NaiveTime::from_hms_nano_opt(3, 5, 7, 98765432).unwrap();
|
||||
let encoded = serialize(&t).unwrap();
|
||||
let decoded: NaiveTime = deserialize(&encoded).unwrap();
|
||||
assert_eq!(t, decoded);
|
||||
}
|
||||
}
|
||||
|
@ -50,15 +50,34 @@ mod inner {
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))]
|
||||
mod inner {
|
||||
use crate::{FixedOffset, LocalResult, NaiveDateTime};
|
||||
use crate::{Datelike, FixedOffset, LocalResult, NaiveDateTime, Timelike};
|
||||
|
||||
pub(super) fn offset_from_utc_datetime(_utc: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
let offset = js_sys::Date::new_0().get_timezone_offset();
|
||||
pub(super) fn offset_from_utc_datetime(utc: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
let offset = js_sys::Date::from(utc.and_utc()).get_timezone_offset();
|
||||
LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
|
||||
}
|
||||
|
||||
pub(super) fn offset_from_local_datetime(local: &NaiveDateTime) -> LocalResult<FixedOffset> {
|
||||
offset_from_utc_datetime(local)
|
||||
let mut year = local.year();
|
||||
if year < 100 {
|
||||
// The API in `js_sys` does not let us create a `Date` with negative years.
|
||||
// And values for years from `0` to `99` map to the years `1900` to `1999`.
|
||||
// Shift the value by a multiple of 400 years until it is `>= 100`.
|
||||
let shift_cycles = (year - 100).div_euclid(400);
|
||||
year -= shift_cycles * 400;
|
||||
}
|
||||
let js_date = js_sys::Date::new_with_year_month_day_hr_min_sec(
|
||||
year as u32,
|
||||
local.month0() as i32,
|
||||
local.day() as i32,
|
||||
local.hour() as i32,
|
||||
local.minute() as i32,
|
||||
local.second() as i32,
|
||||
// ignore milliseconds, our representation of leap seconds may be problematic
|
||||
);
|
||||
let offset = js_date.get_timezone_offset();
|
||||
// We always get a result, even if this time does not exist or is ambiguous.
|
||||
LocalResult::Single(FixedOffset::west_opt((offset as i32) * 60).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,33 +114,9 @@ impl Local {
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date and time.
|
||||
#[cfg(not(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
)))]
|
||||
#[must_use]
|
||||
pub fn now() -> DateTime<Local> {
|
||||
Utc::now().with_timezone(&Local)
|
||||
}
|
||||
|
||||
/// Returns a `DateTime` which corresponds to the current date and time.
|
||||
#[cfg(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))]
|
||||
#[must_use]
|
||||
pub fn now() -> DateTime<Local> {
|
||||
use super::Utc;
|
||||
let now: DateTime<Utc> = super::Utc::now();
|
||||
|
||||
// Workaround missing timezone logic in `time` crate
|
||||
let offset =
|
||||
FixedOffset::west_opt((js_sys::Date::new_0().get_timezone_offset() as i32) * 60)
|
||||
.unwrap();
|
||||
DateTime::from_utc(now.naive_utc(), offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone for Local {
|
||||
|
@ -321,7 +321,7 @@ impl<'a> TimeZoneRef<'a> {
|
||||
// Check leap seconds
|
||||
if !(self.leap_seconds.is_empty()
|
||||
|| self.leap_seconds[0].unix_leap_time >= 0
|
||||
&& saturating_abs(self.leap_seconds[0].correction) == 1)
|
||||
&& self.leap_seconds[0].correction.saturating_abs() == 1)
|
||||
{
|
||||
return Err(Error::TimeZone("invalid leap second"));
|
||||
}
|
||||
@ -336,7 +336,7 @@ impl<'a> TimeZoneRef<'a> {
|
||||
|
||||
let diff_unix_leap_time = x1.unix_leap_time.saturating_sub(x0.unix_leap_time);
|
||||
let abs_diff_correction =
|
||||
saturating_abs(x1.correction.saturating_sub(x0.correction));
|
||||
x1.correction.saturating_sub(x0.correction).saturating_abs();
|
||||
|
||||
if !(diff_unix_leap_time >= min_interval && abs_diff_correction == 1) {
|
||||
return Err(Error::TimeZone("invalid leap second"));
|
||||
@ -615,17 +615,6 @@ fn find_tz_file(path: impl AsRef<Path>) -> Result<File, Error> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn saturating_abs(v: i32) -> i32 {
|
||||
if v.is_positive() {
|
||||
v
|
||||
} else if v == i32::min_value() {
|
||||
i32::max_value()
|
||||
} else {
|
||||
-v
|
||||
}
|
||||
}
|
||||
|
||||
// Possible system timezone directories
|
||||
#[cfg(unix)]
|
||||
const ZONE_INFO_DIRECTORIES: [&str; 4] =
|
||||
|
@ -53,7 +53,7 @@ pub trait Datelike: Sized {
|
||||
/// Returns the ISO week.
|
||||
fn iso_week(&self) -> IsoWeek;
|
||||
|
||||
/// Makes a new value with the year number changed.
|
||||
/// Makes a new value with the year number changed, while keeping the same month and day.
|
||||
///
|
||||
/// Returns `None` when the resulting value would be invalid.
|
||||
fn with_year(&self, year: i32) -> Option<Self>;
|
||||
|
@ -195,43 +195,6 @@ impl fmt::Debug for ParseWeekdayError {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Weekday;
|
||||
|
||||
#[test]
|
||||
fn test_num_days_from() {
|
||||
for i in 0..7 {
|
||||
let base_day = Weekday::try_from(i).unwrap();
|
||||
|
||||
assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
|
||||
assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));
|
||||
|
||||
assert_eq!(base_day.num_days_from(base_day), 0);
|
||||
|
||||
assert_eq!(base_day.num_days_from(base_day.pred()), 1);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5);
|
||||
assert_eq!(
|
||||
base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()),
|
||||
6
|
||||
);
|
||||
|
||||
assert_eq!(base_day.num_days_from(base_day.succ()), 6);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2);
|
||||
assert_eq!(
|
||||
base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
@ -275,8 +238,46 @@ mod weekday_serde {
|
||||
deserializer.deserialize_str(WeekdayVisitor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Weekday;
|
||||
|
||||
#[test]
|
||||
fn test_num_days_from() {
|
||||
for i in 0..7 {
|
||||
let base_day = Weekday::try_from(i).unwrap();
|
||||
|
||||
assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
|
||||
assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));
|
||||
|
||||
assert_eq!(base_day.num_days_from(base_day), 0);
|
||||
|
||||
assert_eq!(base_day.num_days_from(base_day.pred()), 1);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4);
|
||||
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5);
|
||||
assert_eq!(
|
||||
base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()),
|
||||
6
|
||||
);
|
||||
|
||||
assert_eq!(base_day.num_days_from(base_day.succ()), 6);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3);
|
||||
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2);
|
||||
assert_eq!(
|
||||
base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_serialize() {
|
||||
use serde_json::to_string;
|
||||
use Weekday::*;
|
||||
@ -298,6 +299,7 @@ mod weekday_serde {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "serde")]
|
||||
fn test_serde_deserialize() {
|
||||
use serde_json::from_str;
|
||||
use Weekday::*;
|
||||
|
4
taplo.toml
Normal file
4
taplo.toml
Normal file
@ -0,0 +1,4 @@
|
||||
include = ["deny.toml", "**/Cargo.toml"]
|
||||
|
||||
[formatting]
|
||||
inline_table_expand = false
|
@ -58,12 +58,13 @@ fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) {
|
||||
/// for testing only
|
||||
#[allow(dead_code)]
|
||||
#[cfg(not(target_os = "aix"))]
|
||||
const DATE_PATH: &'static str = "/usr/bin/date";
|
||||
const DATE_PATH: &str = "/usr/bin/date";
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_os = "aix")]
|
||||
const DATE_PATH: &'static str = "/opt/freeware/bin/date";
|
||||
const DATE_PATH: &str = "/opt/freeware/bin/date";
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(unix)]
|
||||
/// test helper to sanity check the date command behaves as expected
|
||||
/// asserts the command succeeded
|
||||
fn assert_run_date_version() {
|
||||
|
@ -1,11 +1,18 @@
|
||||
//! Run this test with:
|
||||
//! `env TZ="$(date +%z)" NOW="$(date +%s)" wasm-pack test --node -- --features wasmbind`
|
||||
//!
|
||||
//! The `TZ` and `NOW` variables are used to compare the results inside the WASM environment with
|
||||
//! the host system.
|
||||
//! The check will fail if the local timezone does not match one of the timezones defined below.
|
||||
|
||||
#![cfg(all(
|
||||
target_arch = "wasm32",
|
||||
feature = "wasmbind",
|
||||
not(any(target_os = "emscripten", target_os = "wasi"))
|
||||
))]
|
||||
|
||||
use self::chrono::prelude::*;
|
||||
use self::wasm_bindgen_test::*;
|
||||
use chrono::prelude::*;
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn now() {
|
||||
@ -17,7 +24,7 @@ fn now() {
|
||||
let actual = Utc.datetime_from_str(&now, "%s").unwrap();
|
||||
let diff = utc - actual;
|
||||
assert!(
|
||||
diff < chrono::Duration::minutes(5),
|
||||
diff < chrono::TimeDelta::minutes(5),
|
||||
"expected {} - {} == {} < 5m (env var: {})",
|
||||
utc,
|
||||
actual,
|
||||
@ -52,7 +59,7 @@ fn from_is_exact() {
|
||||
|
||||
let dt = DateTime::<Utc>::from(now.clone());
|
||||
|
||||
assert_eq!(now.get_time() as i64, dt.timestamp_millis_opt().unwrap());
|
||||
assert_eq!(now.get_time() as i64, dt.timestamp_millis());
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
@ -72,7 +79,7 @@ fn convert_all_parts_with_milliseconds() {
|
||||
let js_date = js_sys::Date::from(time);
|
||||
|
||||
assert_eq!(js_date.get_utc_full_year(), 2020);
|
||||
assert_eq!(js_date.get_utc_month(), 12);
|
||||
assert_eq!(js_date.get_utc_month(), 11); // months are numbered 0..=11
|
||||
assert_eq!(js_date.get_utc_date(), 1);
|
||||
assert_eq!(js_date.get_utc_hours(), 3);
|
||||
assert_eq!(js_date.get_utc_minutes(), 1);
|
||||
|
Loading…
x
Reference in New Issue
Block a user