Merge 0.4.x into main

This commit is contained in:
Dirkjan Ochtman 2022-11-22 13:41:09 -08:00
commit b1ab498363
30 changed files with 1141 additions and 712 deletions

View File

@ -33,6 +33,7 @@ pure-rust-locales = { version = "0.5.2", optional = true }
criterion = { version = "0.4.0", optional = true } criterion = { version = "0.4.0", optional = true }
rkyv = {version = "0.7", optional = true} rkyv = {version = "0.7", optional = true}
iana-time-zone = { version = "0.1.44", optional = true, features = ["fallback"] } iana-time-zone = { version = "0.1.44", optional = true, features = ["fallback"] }
arbitrary = { version = "1.0.0", features = ["derive"], optional = true }
[target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies] [target.'cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))'.dependencies]
wasm-bindgen = { version = "0.2", optional = true } wasm-bindgen = { version = "0.2", optional = true }

View File

@ -6,7 +6,7 @@
[![Chrono on docs.rs][docsrs-image]][docsrs] [![Chrono on docs.rs][docsrs-image]][docsrs]
[![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter] [![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter]
[gh-image]: https://github.com/chronotope/chrono/workflows/test/badge.svg [gh-image]: https://github.com/chronotope/chrono/actions/workflows/test.yml/badge.svg
[gh-checks]: https://github.com/chronotope/chrono/actions?query=workflow%3Atest [gh-checks]: https://github.com/chronotope/chrono/actions?query=workflow%3Atest
[cratesio-image]: https://img.shields.io/crates/v/chrono.svg [cratesio-image]: https://img.shields.io/crates/v/chrono.svg
[cratesio]: https://crates.io/crates/chrono [cratesio]: https://crates.io/crates/chrono

View File

@ -4,7 +4,7 @@
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use chrono::prelude::*; use chrono::prelude::*;
use chrono::{DateTime, FixedOffset, Utc, __BenchYearFlags}; use chrono::{DateTime, FixedOffset, Local, Utc, __BenchYearFlags};
fn bench_datetime_parse_from_rfc2822(c: &mut Criterion) { fn bench_datetime_parse_from_rfc2822(c: &mut Criterion) {
c.bench_function("bench_datetime_parse_from_rfc2822", |b| { c.bench_function("bench_datetime_parse_from_rfc2822", |b| {
@ -36,13 +36,27 @@ fn bench_datetime_from_str(c: &mut Criterion) {
fn bench_datetime_to_rfc2822(c: &mut Criterion) { fn bench_datetime_to_rfc2822(c: &mut Criterion) {
let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
let dt = pst.ymd_opt(2018, 1, 11).unwrap().and_hms_nano_opt(10, 5, 13, 84_660_000).unwrap(); let dt = pst
.from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 13, 84_660_000)
.unwrap(),
)
.unwrap();
c.bench_function("bench_datetime_to_rfc2822", |b| b.iter(|| black_box(dt).to_rfc2822())); c.bench_function("bench_datetime_to_rfc2822", |b| b.iter(|| black_box(dt).to_rfc2822()));
} }
fn bench_datetime_to_rfc3339(c: &mut Criterion) { fn bench_datetime_to_rfc3339(c: &mut Criterion) {
let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
let dt = pst.ymd_opt(2018, 1, 11).and_hms_nano_opt(10, 5, 13, 84_660_000).unwrap(); let dt = pst
.from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 13, 84_660_000)
.unwrap(),
)
.unwrap();
c.bench_function("bench_datetime_to_rfc3339", |b| b.iter(|| black_box(dt).to_rfc3339())); c.bench_function("bench_datetime_to_rfc3339", |b| b.iter(|| black_box(dt).to_rfc3339()));
} }
@ -56,6 +70,14 @@ fn bench_year_flags_from_year(c: &mut Criterion) {
}); });
} }
fn bench_get_local_time(c: &mut Criterion) {
c.bench_function("bench_get_local_time", |b| {
b.iter(|| {
let _ = Local::now();
})
});
}
/// Returns the number of multiples of `div` in the range `start..end`. /// Returns the number of multiples of `div` in the range `start..end`.
/// ///
/// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the
@ -109,6 +131,7 @@ criterion_group!(
bench_datetime_to_rfc3339, bench_datetime_to_rfc3339,
bench_year_flags_from_year, bench_year_flags_from_year,
bench_num_days_from_ce, bench_num_days_from_ce,
bench_get_local_time,
); );
criterion_main!(benches); criterion_main!(benches);

View File

@ -3,5 +3,5 @@
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
pub fn create_time() { pub fn create_time() {
let _ = Utc.ymd_opt(2019, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); let _ = Utc.with_ymd_and_hms(2019, 1, 1, 0, 0, 0).unwrap();
} }

View File

@ -2,6 +2,7 @@
// See README.md and LICENSE.txt for details. // See README.md and LICENSE.txt for details.
//! ISO 8601 calendar date with time zone. //! ISO 8601 calendar date with time zone.
#![allow(deprecated)]
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow; use core::borrow::Borrow;
@ -53,6 +54,7 @@ use crate::{Datelike, Weekday};
/// - The date is timezone-agnostic up to one day (i.e. practically always), /// - The date is timezone-agnostic up to one day (i.e. practically always),
/// so the local date and UTC date should be equal for most cases /// so the local date and UTC date should be equal for most cases
/// even though the raw calculation between `NaiveDate` and `Duration` may not. /// even though the raw calculation between `NaiveDate` and `Duration` may not.
#[deprecated(since = "0.4.23", note = "Use `NaiveDate` or `DateTime<Tz>` instead")]
#[derive(Clone)] #[derive(Clone)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] #[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
pub struct Date<Tz: TimeZone> { pub struct Date<Tz: TimeZone> {
@ -288,15 +290,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Returns the number of whole years from the given `base` until `self`. /// Returns the number of whole years from the given `base` until `self`.
pub fn years_since(&self, base: Self) -> Option<u32> { pub fn years_since(&self, base: Self) -> Option<u32> {
let mut years = self.year() - base.year(); self.date.years_since(base.date)
if (self.month(), self.day()) < (base.month(), base.day()) {
years -= 1;
}
match years >= 0 {
true => Some(years as u32),
false => None,
}
} }
/// The minimum possible `Date`. /// The minimum possible `Date`.
@ -332,15 +326,6 @@ where
/// Formats the date with the specified format string. /// Formats the date with the specified format string.
/// See the [`crate::format::strftime`] module /// See the [`crate::format::strftime`] module
/// on the supported escape sequences. /// on the supported escape sequences.
///
/// # Example
/// ```rust
/// use chrono::prelude::*;
///
/// let date_time: Date<Utc> = Utc.ymd_opt(2017, 04, 02).unwrap();
/// let formatted = format!("{}", date_time.format("%d/%m/%Y"));
/// assert_eq!(formatted, "02/04/2017");
/// ```
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline] #[inline]
@ -532,7 +517,8 @@ impl<Tz: TimeZone> Sub<Date<Tz>> for Date<Tz> {
impl<Tz: TimeZone> fmt::Debug for Date<Tz> { impl<Tz: TimeZone> fmt::Debug for Date<Tz> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.naive_local(), self.offset) self.naive_local().fmt(f)?;
self.offset.fmt(f)
} }
} }
@ -541,7 +527,23 @@ where
Tz::Offset: fmt::Display, Tz::Offset: fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.naive_local(), self.offset) self.naive_local().fmt(f)?;
self.offset.fmt(f)
}
}
// Note that implementation of Arbitrary cannot be automatically derived for Date<Tz>, due to
// the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary.
#[cfg(feature = "arbitrary")]
impl<'a, Tz> arbitrary::Arbitrary<'a> for Date<Tz>
where
Tz: TimeZone,
<Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Date<Tz>> {
let date = NaiveDate::arbitrary(u)?;
let offset = <Tz as TimeZone>::Offset::arbitrary(u)?;
Ok(Date::from_utc(date, offset))
} }
} }

View File

@ -11,6 +11,7 @@ use alloc::string::{String, ToString};
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow; use core::borrow::Borrow;
use core::cmp::Ordering; use core::cmp::Ordering;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::{fmt, hash, str}; use core::{fmt, hash, str};
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -31,7 +32,9 @@ use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime};
#[cfg(feature = "clock")] #[cfg(feature = "clock")]
use crate::offset::Local; use crate::offset::Local;
use crate::offset::{FixedOffset, Offset, TimeZone, Utc}; use crate::offset::{FixedOffset, Offset, TimeZone, Utc};
use crate::{Date, Datelike, Months, TimeDelta, Timelike, Weekday}; #[allow(deprecated)]
use crate::Date;
use crate::{Datelike, Months, TimeDelta, Timelike, Weekday};
/// documented at re-export site /// documented at re-export site
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -145,18 +148,9 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Unless you are immediately planning on turning this into a `DateTime` /// Unless you are immediately planning on turning this into a `DateTime`
/// with the same Timezone you should use the /// with the same Timezone you should use the
/// [`date_naive`](DateTime::date_naive) method. /// [`date_naive`](DateTime::date_naive) method.
///
/// ```
/// use chrono::prelude::*;
///
/// let date: Date<Utc> = Utc.ymd_opt(2020, 1, 1).unwrap();
/// let dt: DateTime<Utc> = date.and_hms_opt(0, 0, 0).unwrap();
///
/// assert_eq!(dt.date(), date);
///
/// assert_eq!(dt.date().and_hms_opt(1, 1, 1).unwrap(), date.and_hms_opt(1, 1, 1).unwrap());
/// ```
#[inline] #[inline]
#[deprecated(since = "0.4.23", note = "Use `date_naive()` instead")]
#[allow(deprecated)]
pub fn date(&self) -> Date<Tz> { pub fn date(&self) -> Date<Tz> {
Date::from_utc(self.naive_local().date(), self.offset.clone()) Date::from_utc(self.naive_local().date(), self.offset.clone())
} }
@ -169,8 +163,8 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// ``` /// ```
/// use chrono::prelude::*; /// use chrono::prelude::*;
/// ///
/// let date: DateTime<Utc> = Utc.ymd_opt(2020, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); /// let date: DateTime<Utc> = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap();
/// let other: DateTime<FixedOffset> = FixedOffset::east_opt(23).unwrap().ymd_opt(2020, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); /// let other: DateTime<FixedOffset> = FixedOffset::east_opt(23).unwrap().with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap();
/// assert_eq!(date.date_naive(), other.date_naive()); /// assert_eq!(date.date_naive(), other.date_naive());
/// ``` /// ```
#[inline] #[inline]
@ -203,13 +197,12 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use chrono::Utc; /// use chrono::{Utc, TimeZone, NaiveDate};
/// use chrono::TimeZone;
/// ///
/// let dt = Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 444).unwrap(); /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_milli_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_millis(), 1_444); /// assert_eq!(dt.timestamp_millis(), 1_444);
/// ///
/// let dt = Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_milli_opt(1, 46, 40, 555).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_milli_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555); /// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555);
/// ``` /// ```
#[inline] #[inline]
@ -227,13 +220,12 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use chrono::Utc; /// use chrono::{Utc, TimeZone, NaiveDate};
/// use chrono::TimeZone;
/// ///
/// let dt = Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_micro_opt(0, 0, 1, 444).unwrap(); /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_micro_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_micros(), 1_000_444); /// assert_eq!(dt.timestamp_micros(), 1_000_444);
/// ///
/// let dt = Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_micro_opt(1, 46, 40, 555).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_micro_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555); /// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555);
/// ``` /// ```
#[inline] #[inline]
@ -251,13 +243,12 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// use chrono::Utc; /// use chrono::{Utc, TimeZone, NaiveDate};
/// use chrono::TimeZone;
/// ///
/// let dt = Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap(); /// let dt = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().and_hms_nano_opt(0, 0, 1, 444).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_nanos(), 1_000_000_444); /// assert_eq!(dt.timestamp_nanos(), 1_000_000_444);
/// ///
/// let dt = Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 40, 555).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555); /// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555);
/// ``` /// ```
#[inline] #[inline]
@ -533,10 +524,10 @@ impl DateTime<FixedOffset> {
/// and email headers. /// and email headers.
/// ///
/// ``` /// ```
/// # use chrono::{DateTime, FixedOffset, TimeZone}; /// # use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate};
/// assert_eq!( /// assert_eq!(
/// DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(), /// DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 GMT").unwrap(),
/// FixedOffset::east_opt(0).unwrap().ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap() /// FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap()
/// ); /// );
/// ``` /// ```
pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> { pub fn parse_from_rfc2822(s: &str) -> ParseResult<DateTime<FixedOffset>> {
@ -579,11 +570,11 @@ impl DateTime<FixedOffset> {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// use chrono::{DateTime, FixedOffset, TimeZone}; /// use chrono::{DateTime, FixedOffset, TimeZone, NaiveDate};
/// ///
/// let dt = DateTime::<FixedOffset>::parse_from_str( /// let dt = DateTime::<FixedOffset>::parse_from_str(
/// "1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z"); /// "1983 Apr 13 12:09:14.274 +0000", "%Y %b %d %H:%M:%S%.3f %z");
/// assert_eq!(dt, Ok(FixedOffset::east_opt(0).unwrap().ymd_opt(1983, 4, 13).unwrap().and_hms_milli_opt(12, 9, 14, 274).unwrap())); /// assert_eq!(dt, Ok(FixedOffset::east_opt(0).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(1983, 4, 13).unwrap().and_hms_milli_opt(12, 9, 14, 274).unwrap()).unwrap()));
/// ``` /// ```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> { pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<DateTime<FixedOffset>> {
let mut parsed = Parsed::new(); let mut parsed = Parsed::new();
@ -656,16 +647,20 @@ where
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
pub fn to_rfc2822(&self) -> String { pub fn to_rfc2822(&self) -> String {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC2822)]; let mut result = String::with_capacity(32);
self.format_with_items(ITEMS.iter()).to_string() crate::format::write_rfc2822(&mut result, self.naive_local(), self.offset.fix())
.expect("writing rfc2822 datetime to string should never fail");
result
} }
/// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`. /// Returns an RFC 3339 and ISO 8601 date and time string such as `1996-12-19T16:39:57-08:00`.
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
pub fn to_rfc3339(&self) -> String { pub fn to_rfc3339(&self) -> String {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)]; let mut result = String::with_capacity(32);
self.format_with_items(ITEMS.iter()).to_string() crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix())
.expect("writing rfc3339 datetime to string should never fail");
result
} }
/// Return an RFC 3339 and ISO 8601 date and time string with subseconds /// Return an RFC 3339 and ISO 8601 date and time string with subseconds
@ -678,8 +673,8 @@ where
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
/// # use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone, Utc}; /// # use chrono::{DateTime, FixedOffset, SecondsFormat, TimeZone, Utc, NaiveDate};
/// let dt = Utc.ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(18, 30, 9, 453_829).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(18, 30, 9, 453_829).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false), /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, false),
/// "2018-01-26T18:30:09.453+00:00"); /// "2018-01-26T18:30:09.453+00:00");
/// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true), /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Millis, true),
@ -688,7 +683,7 @@ where
/// "2018-01-26T18:30:09Z"); /// "2018-01-26T18:30:09Z");
/// ///
/// let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); /// let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
/// let dt = pst.ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(10, 30, 9, 453_829).unwrap(); /// let dt = pst.from_local_datetime(&NaiveDate::from_ymd_opt(2018, 1, 26).unwrap().and_hms_micro_opt(10, 30, 9, 453_829).unwrap()).unwrap();
/// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true), /// assert_eq!(dt.to_rfc3339_opts(SecondsFormat::Secs, true),
/// "2018-01-26T10:30:09+08:00"); /// "2018-01-26T10:30:09+08:00");
/// ``` /// ```
@ -757,7 +752,7 @@ where
/// ```rust /// ```rust
/// use chrono::prelude::*; /// use chrono::prelude::*;
/// ///
/// let date_time: DateTime<Utc> = Utc.ymd_opt(2017, 04, 02).unwrap().and_hms_opt(12, 50, 32).unwrap(); /// let date_time: DateTime<Utc> = Utc.with_ymd_and_hms(2017, 04, 02, 12, 50, 32).unwrap();
/// let formatted = format!("{}", date_time.format("%d/%m/%Y %H:%M")); /// let formatted = format!("{}", date_time.format("%d/%m/%Y %H:%M"));
/// assert_eq!(formatted, "02/04/2017 12:50"); /// assert_eq!(formatted, "02/04/2017 12:50");
/// ``` /// ```
@ -941,8 +936,8 @@ impl<Tz: TimeZone, Tz2: TimeZone> PartialOrd<DateTime<Tz2>> for DateTime<Tz> {
/// ``` /// ```
/// use chrono::prelude::*; /// use chrono::prelude::*;
/// ///
/// let earlier = Utc.ymd_opt(2015, 5, 15).unwrap().and_hms_opt(2, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(1 * 3600).unwrap()); /// let earlier = Utc.with_ymd_and_hms(2015, 5, 15, 2, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(1 * 3600).unwrap());
/// let later = Utc.ymd_opt(2015, 5, 15).unwrap().and_hms_opt(3, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(5 * 3600).unwrap()); /// let later = Utc.with_ymd_and_hms(2015, 5, 15, 3, 0, 0).unwrap().with_timezone(&FixedOffset::west_opt(5 * 3600).unwrap());
/// ///
/// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00"); /// assert_eq!(earlier.to_string(), "2015-05-15 01:00:00 -01:00");
/// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00"); /// assert_eq!(later.to_string(), "2015-05-14 22:00:00 -05:00");
@ -1047,7 +1042,8 @@ impl<Tz: TimeZone> Sub<Days> for DateTime<Tz> {
impl<Tz: TimeZone> fmt::Debug for DateTime<Tz> { impl<Tz: TimeZone> fmt::Debug for DateTime<Tz> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}{:?}", self.naive_local(), self.offset) self.naive_local().fmt(f)?;
self.offset.fmt(f)
} }
} }
@ -1056,7 +1052,9 @@ where
Tz::Offset: fmt::Display, Tz::Offset: fmt::Display,
{ {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.naive_local(), self.offset) self.naive_local().fmt(f)?;
f.write_char(' ')?;
self.offset.fmt(f)
} }
} }
@ -1207,49 +1205,44 @@ impl From<DateTime<Utc>> for js_sys::Date {
} }
} }
// Note that implementation of Arbitrary cannot be simply derived for DateTime<Tz>, due to
// the nontrivial bound <Tz as TimeZone>::Offset: Arbitrary.
#[cfg(feature = "arbitrary")]
impl<'a, Tz> arbitrary::Arbitrary<'a> for DateTime<Tz>
where
Tz: TimeZone,
<Tz as TimeZone>::Offset: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<DateTime<Tz>> {
let datetime = NaiveDateTime::arbitrary(u)?;
let offset = <Tz as TimeZone>::Offset::arbitrary(u)?;
Ok(DateTime::from_utc(datetime, offset))
}
}
#[test] #[test]
fn test_add_sub_months() { fn test_add_sub_months() {
let utc_dt = Utc.ymd_opt(2018, 9, 5).unwrap().and_hms_opt(23, 58, 0).unwrap(); let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
assert_eq!( assert_eq!(utc_dt + Months::new(15), Utc.with_ymd_and_hms(2019, 12, 5, 23, 58, 0).unwrap());
utc_dt + Months::new(15),
Utc.ymd_opt(2019, 12, 5).unwrap().and_hms_opt(23, 58, 0).unwrap()
);
let utc_dt = Utc.ymd_opt(2020, 1, 31).unwrap().and_hms_opt(23, 58, 0).unwrap(); let utc_dt = Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap();
assert_eq!( assert_eq!(utc_dt + Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
utc_dt + Months::new(1), assert_eq!(utc_dt + Months::new(2), Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap());
Utc.ymd_opt(2020, 2, 29).unwrap().and_hms_opt(23, 58, 0).unwrap()
);
assert_eq!(
utc_dt + Months::new(2),
Utc.ymd_opt(2020, 3, 31).unwrap().and_hms_opt(23, 58, 0).unwrap()
);
let utc_dt = Utc.ymd_opt(2018, 9, 5).unwrap().and_hms_opt(23, 58, 0).unwrap(); let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
assert_eq!( assert_eq!(utc_dt - Months::new(15), Utc.with_ymd_and_hms(2017, 6, 5, 23, 58, 0).unwrap());
utc_dt - Months::new(15),
Utc.ymd_opt(2017, 6, 5).unwrap().and_hms_opt(23, 58, 0).unwrap()
);
let utc_dt = Utc.ymd_opt(2020, 3, 31).unwrap().and_hms_opt(23, 58, 0).unwrap(); let utc_dt = Utc.with_ymd_and_hms(2020, 3, 31, 23, 58, 0).unwrap();
assert_eq!( assert_eq!(utc_dt - Months::new(1), Utc.with_ymd_and_hms(2020, 2, 29, 23, 58, 0).unwrap());
utc_dt - Months::new(1), assert_eq!(utc_dt - Months::new(2), Utc.with_ymd_and_hms(2020, 1, 31, 23, 58, 0).unwrap());
Utc.ymd_opt(2020, 2, 29).unwrap().and_hms_opt(23, 58, 0).unwrap()
);
assert_eq!(
utc_dt - Months::new(2),
Utc.ymd_opt(2020, 1, 31).unwrap().and_hms_opt(23, 58, 0).unwrap()
);
} }
#[test] #[test]
fn test_auto_conversion() { fn test_auto_conversion() {
let utc_dt = Utc.ymd_opt(2018, 9, 5).unwrap().and_hms_opt(23, 58, 0).unwrap(); let utc_dt = Utc.with_ymd_and_hms(2018, 9, 5, 23, 58, 0).unwrap();
let cdt_dt = FixedOffset::west_opt(5 * 60 * 60) let cdt_dt = FixedOffset::west_opt(5 * 60 * 60)
.unwrap() .unwrap()
.ymd_opt(2018, 9, 5) .with_ymd_and_hms(2018, 9, 5, 18, 58, 0)
.unwrap()
.and_hms_opt(18, 58, 0)
.unwrap(); .unwrap();
let utc_dt2: DateTime<Utc> = cdt_dt.into(); let utc_dt2: DateTime<Utc> = cdt_dt.into();
assert_eq!(utc_dt, utc_dt2); assert_eq!(utc_dt, utc_dt2);
@ -1263,30 +1256,20 @@ where
E: ::core::fmt::Debug, E: ::core::fmt::Debug,
{ {
assert_eq!( assert_eq!(
to_string_utc(&Utc.ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap()).ok(), to_string_utc(&Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()).ok(),
Some(r#""2014-07-24T12:34:06Z""#.into()) Some(r#""2014-07-24T12:34:06Z""#.into())
); );
assert_eq!( assert_eq!(
to_string_fixed( to_string_fixed(
&FixedOffset::east_opt(3660) &FixedOffset::east_opt(3660).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()
.unwrap()
.ymd_opt(2014, 7, 24)
.unwrap()
.and_hms_opt(12, 34, 6)
.unwrap()
) )
.ok(), .ok(),
Some(r#""2014-07-24T12:34:06+01:01""#.into()) Some(r#""2014-07-24T12:34:06+01:01""#.into())
); );
assert_eq!( assert_eq!(
to_string_fixed( to_string_fixed(
&FixedOffset::east_opt(3650) &FixedOffset::east_opt(3650).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()
.unwrap()
.ymd_opt(2014, 7, 24)
.unwrap()
.and_hms_opt(12, 34, 6)
.unwrap()
) )
.ok(), .ok(),
Some(r#""2014-07-24T12:34:06+01:00:50""#.into()) Some(r#""2014-07-24T12:34:06+01:00:50""#.into())
@ -1311,22 +1294,17 @@ fn test_decodable_json<FUtc, FFixed, FLocal, E>(
assert_eq!( assert_eq!(
norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()), norm(&utc_from_str(r#""2014-07-24T12:34:06Z""#).ok()),
norm(&Some(Utc.ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap())) norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()))
); );
assert_eq!( assert_eq!(
norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()), norm(&utc_from_str(r#""2014-07-24T13:57:06+01:23""#).ok()),
norm(&Some(Utc.ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap())) norm(&Some(Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()))
); );
assert_eq!( assert_eq!(
norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()), norm(&fixed_from_str(r#""2014-07-24T12:34:06Z""#).ok()),
norm(&Some( norm(&Some(
FixedOffset::east_opt(0) FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()
.unwrap()
.ymd_opt(2014, 7, 24)
.unwrap()
.and_hms_opt(12, 34, 6)
.unwrap()
)) ))
); );
assert_eq!( assert_eq!(
@ -1334,9 +1312,7 @@ fn test_decodable_json<FUtc, FFixed, FLocal, E>(
norm(&Some( norm(&Some(
FixedOffset::east_opt(60 * 60 + 23 * 60) FixedOffset::east_opt(60 * 60 + 23 * 60)
.unwrap() .unwrap()
.ymd_opt(2014, 7, 24) .with_ymd_and_hms(2014, 7, 24, 13, 57, 6)
.unwrap()
.and_hms_opt(13, 57, 6)
.unwrap() .unwrap()
)) ))
); );
@ -1345,11 +1321,11 @@ fn test_decodable_json<FUtc, FFixed, FLocal, E>(
// the conversion didn't change the instant itself // the conversion didn't change the instant itself
assert_eq!( assert_eq!(
local_from_str(r#""2014-07-24T12:34:06Z""#).expect("local shouuld parse"), local_from_str(r#""2014-07-24T12:34:06Z""#).expect("local shouuld parse"),
Utc.ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap() Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()
); );
assert_eq!( assert_eq!(
local_from_str(r#""2014-07-24T13:57:06+01:23""#).expect("local should parse with offset"), local_from_str(r#""2014-07-24T13:57:06+01:23""#).expect("local should parse with offset"),
Utc.ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap() Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()
); );
assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err()); assert!(utc_from_str(r#""2014-07-32T12:34:06Z""#).is_err());

View File

@ -106,7 +106,7 @@ impl<'de> de::Deserialize<'de> for DateTime<Local> {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::{Deserialize, Serialize}; /// # use serde_derive::{Deserialize, Serialize};
/// use chrono::serde::ts_nanoseconds; /// use chrono::serde::ts_nanoseconds;
/// #[derive(Deserialize, Serialize)] /// #[derive(Deserialize, Serialize)]
@ -115,7 +115,7 @@ impl<'de> de::Deserialize<'de> for DateTime<Local> {
/// time: DateTime<Utc> /// time: DateTime<Utc>
/// } /// }
/// ///
/// let time = Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap(); /// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap();
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -142,7 +142,7 @@ pub mod ts_nanoseconds {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::Serialize; /// # use serde_derive::Serialize;
/// use chrono::serde::ts_nanoseconds::serialize as to_nano_ts; /// use chrono::serde::ts_nanoseconds::serialize as to_nano_ts;
/// #[derive(Serialize)] /// #[derive(Serialize)]
@ -152,7 +152,7 @@ pub mod ts_nanoseconds {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap(), /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap(),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
@ -231,7 +231,7 @@ pub mod ts_nanoseconds {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::{Deserialize, Serialize}; /// # use serde_derive::{Deserialize, Serialize};
/// use chrono::serde::ts_nanoseconds_option; /// use chrono::serde::ts_nanoseconds_option;
/// #[derive(Deserialize, Serialize)] /// #[derive(Deserialize, Serialize)]
@ -240,7 +240,7 @@ pub mod ts_nanoseconds {
/// time: Option<DateTime<Utc>> /// time: Option<DateTime<Utc>>
/// } /// }
/// ///
/// let time = Some(Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap()); /// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap());
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -266,7 +266,7 @@ pub mod ts_nanoseconds_option {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::Serialize; /// # use serde_derive::Serialize;
/// use chrono::serde::ts_nanoseconds_option::serialize as to_nano_tsopt; /// use chrono::serde::ts_nanoseconds_option::serialize as to_nano_tsopt;
/// #[derive(Serialize)] /// #[derive(Serialize)]
@ -276,7 +276,7 @@ pub mod ts_nanoseconds_option {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Some(Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap()), /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_nano_opt(02, 04, 59, 918355733).unwrap().and_local_timezone(Utc).unwrap()),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#); /// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
@ -360,7 +360,7 @@ pub mod ts_nanoseconds_option {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::{Deserialize, Serialize}; /// # use serde_derive::{Deserialize, Serialize};
/// use chrono::serde::ts_microseconds; /// use chrono::serde::ts_microseconds;
/// #[derive(Deserialize, Serialize)] /// #[derive(Deserialize, Serialize)]
@ -369,7 +369,7 @@ pub mod ts_nanoseconds_option {
/// time: DateTime<Utc> /// time: DateTime<Utc>
/// } /// }
/// ///
/// let time = Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap(); /// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap();
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -395,7 +395,7 @@ pub mod ts_microseconds {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::Serialize; /// # use serde_derive::Serialize;
/// use chrono::serde::ts_microseconds::serialize as to_micro_ts; /// use chrono::serde::ts_microseconds::serialize as to_micro_ts;
/// #[derive(Serialize)] /// #[derive(Serialize)]
@ -405,7 +405,7 @@ pub mod ts_microseconds {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap(), /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap(),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); /// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
@ -484,7 +484,7 @@ pub mod ts_microseconds {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::{Deserialize, Serialize}; /// # use serde_derive::{Deserialize, Serialize};
/// use chrono::serde::ts_microseconds_option; /// use chrono::serde::ts_microseconds_option;
/// #[derive(Deserialize, Serialize)] /// #[derive(Deserialize, Serialize)]
@ -493,7 +493,7 @@ pub mod ts_microseconds {
/// time: Option<DateTime<Utc>> /// time: Option<DateTime<Utc>>
/// } /// }
/// ///
/// let time = Some(Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap()); /// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap());
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -518,7 +518,7 @@ pub mod ts_microseconds_option {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::Serialize; /// # use serde_derive::Serialize;
/// use chrono::serde::ts_microseconds_option::serialize as to_micro_tsopt; /// use chrono::serde::ts_microseconds_option::serialize as to_micro_tsopt;
/// #[derive(Serialize)] /// #[derive(Serialize)]
@ -528,7 +528,7 @@ pub mod ts_microseconds_option {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Some(Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap()), /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_micro_opt(02, 04, 59, 918355).unwrap().and_local_timezone(Utc).unwrap()),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#); /// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
@ -612,7 +612,7 @@ pub mod ts_microseconds_option {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::{Deserialize, Serialize}; /// # use serde_derive::{Deserialize, Serialize};
/// use chrono::serde::ts_milliseconds; /// use chrono::serde::ts_milliseconds;
/// #[derive(Deserialize, Serialize)] /// #[derive(Deserialize, Serialize)]
@ -621,7 +621,7 @@ pub mod ts_microseconds_option {
/// time: DateTime<Utc> /// time: DateTime<Utc>
/// } /// }
/// ///
/// let time = Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(); /// let time = NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap();
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -647,7 +647,7 @@ pub mod ts_milliseconds {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::Serialize; /// # use serde_derive::Serialize;
/// use chrono::serde::ts_milliseconds::serialize as to_milli_ts; /// use chrono::serde::ts_milliseconds::serialize as to_milli_ts;
/// #[derive(Serialize)] /// #[derive(Serialize)]
@ -657,7 +657,7 @@ pub mod ts_milliseconds {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap(), /// time: NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap(),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918}"#); /// assert_eq!(as_string, r#"{"time":1526522699918}"#);
@ -733,7 +733,7 @@ pub mod ts_milliseconds {
/// # Example /// # Example
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::{Deserialize, Serialize}; /// # use serde_derive::{Deserialize, Serialize};
/// use chrono::serde::ts_milliseconds_option; /// use chrono::serde::ts_milliseconds_option;
/// #[derive(Deserialize, Serialize)] /// #[derive(Deserialize, Serialize)]
@ -742,7 +742,7 @@ pub mod ts_milliseconds {
/// time: Option<DateTime<Utc>> /// time: Option<DateTime<Utc>>
/// } /// }
/// ///
/// let time = Some(Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap()); /// let time = Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap());
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -767,7 +767,7 @@ pub mod ts_milliseconds_option {
/// # Example: /// # Example:
/// ///
/// ```rust /// ```rust
/// # use chrono::{TimeZone, DateTime, Utc}; /// # use chrono::{TimeZone, DateTime, Utc, NaiveDate};
/// # use serde_derive::Serialize; /// # use serde_derive::Serialize;
/// use chrono::serde::ts_milliseconds_option::serialize as to_milli_tsopt; /// use chrono::serde::ts_milliseconds_option::serialize as to_milli_tsopt;
/// #[derive(Serialize)] /// #[derive(Serialize)]
@ -777,7 +777,7 @@ pub mod ts_milliseconds_option {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Some(Utc.ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap()), /// time: Some(NaiveDate::from_ymd_opt(2018, 5, 17).unwrap().and_hms_milli_opt(02, 04, 59, 918).unwrap().and_local_timezone(Utc).unwrap()),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1526522699918}"#); /// assert_eq!(as_string, r#"{"time":1526522699918}"#);
@ -883,7 +883,7 @@ pub mod ts_milliseconds_option {
/// time: DateTime<Utc> /// time: DateTime<Utc>
/// } /// }
/// ///
/// let time = Utc.ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(); /// let time = Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap();
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -919,7 +919,7 @@ pub mod ts_seconds {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Utc.ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap(), /// time: Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap(),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1431684000}"#); /// assert_eq!(as_string, r#"{"time":1431684000}"#);
@ -1001,7 +1001,7 @@ pub mod ts_seconds {
/// time: Option<DateTime<Utc>> /// time: Option<DateTime<Utc>>
/// } /// }
/// ///
/// let time = Some(Utc.ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap()); /// let time = Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap());
/// let my_s = S { /// let my_s = S {
/// time: time.clone(), /// time: time.clone(),
/// }; /// };
@ -1036,7 +1036,7 @@ pub mod ts_seconds_option {
/// } /// }
/// ///
/// let my_s = S { /// let my_s = S {
/// time: Some(Utc.ymd_opt(2015, 5, 15).unwrap().and_hms_opt(10, 0, 0).unwrap()), /// time: Some(Utc.with_ymd_and_hms(2015, 5, 15, 10, 0, 0).unwrap()),
/// }; /// };
/// let as_string = serde_json::to_string(&my_s)?; /// let as_string = serde_json::to_string(&my_s)?;
/// assert_eq!(as_string, r#"{"time":1431684000}"#); /// assert_eq!(as_string, r#"{"time":1431684000}"#);
@ -1134,7 +1134,7 @@ fn test_serde_bincode() {
// it is not self-describing. // it is not self-describing.
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
let dt = Utc.ymd_opt(2014, 7, 24).unwrap().and_hms_opt(12, 34, 6).unwrap(); let dt = Utc.with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap();
let encoded = serialize(&dt).unwrap(); let encoded = serialize(&dt).unwrap();
let decoded: DateTime<Utc> = deserialize(&encoded).unwrap(); let decoded: DateTime<Utc> = deserialize(&encoded).unwrap();
assert_eq!(dt, decoded); assert_eq!(dt, decoded);

View File

@ -16,96 +16,90 @@ fn test_datetime_offset() {
let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap(); let kst = FixedOffset::east_opt(9 * 60 * 60).unwrap();
assert_eq!( assert_eq!(
format!("{}", Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap()), format!("{}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
"2014-05-06 07:08:09 UTC" "2014-05-06 07:08:09 UTC"
); );
assert_eq!( assert_eq!(
format!("{}", edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap()), format!("{}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
"2014-05-06 07:08:09 -04:00" "2014-05-06 07:08:09 -04:00"
); );
assert_eq!( assert_eq!(
format!("{}", kst.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap()), format!("{}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
"2014-05-06 07:08:09 +09:00" "2014-05-06 07:08:09 +09:00"
); );
assert_eq!( assert_eq!(
format!("{:?}", Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap()), format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
"2014-05-06T07:08:09Z" "2014-05-06T07:08:09Z"
); );
assert_eq!( assert_eq!(
format!("{:?}", edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap()), format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
"2014-05-06T07:08:09-04:00" "2014-05-06T07:08:09-04:00"
); );
assert_eq!( assert_eq!(
format!("{:?}", kst.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap()), format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap()),
"2014-05-06T07:08:09+09:00" "2014-05-06T07:08:09+09:00"
); );
// edge cases // edge cases
assert_eq!( assert_eq!(
format!("{:?}", Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(0, 0, 0).unwrap()), format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()),
"2014-05-06T00:00:00Z" "2014-05-06T00:00:00Z"
); );
assert_eq!( assert_eq!(
format!("{:?}", edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(0, 0, 0).unwrap()), format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()),
"2014-05-06T00:00:00-04:00" "2014-05-06T00:00:00-04:00"
); );
assert_eq!( assert_eq!(
format!("{:?}", kst.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(0, 0, 0).unwrap()), format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 0, 0, 0).unwrap()),
"2014-05-06T00:00:00+09:00" "2014-05-06T00:00:00+09:00"
); );
assert_eq!( assert_eq!(
format!("{:?}", Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(23, 59, 59).unwrap()), format!("{:?}", Utc.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()),
"2014-05-06T23:59:59Z" "2014-05-06T23:59:59Z"
); );
assert_eq!( assert_eq!(
format!("{:?}", edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(23, 59, 59).unwrap()), format!("{:?}", edt.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()),
"2014-05-06T23:59:59-04:00" "2014-05-06T23:59:59-04:00"
); );
assert_eq!( assert_eq!(
format!("{:?}", kst.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(23, 59, 59).unwrap()), format!("{:?}", kst.with_ymd_and_hms(2014, 5, 6, 23, 59, 59).unwrap()),
"2014-05-06T23:59:59+09:00" "2014-05-06T23:59:59+09:00"
); );
let dt = Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap(); let dt = Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
assert_eq!(dt, edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(3, 8, 9).unwrap()); assert_eq!(dt, edt.with_ymd_and_hms(2014, 5, 6, 3, 8, 9).unwrap());
assert_eq!( assert_eq!(
dt + TimeDelta::seconds(3600 + 60 + 1), dt + TimeDelta::seconds(3600 + 60 + 1),
Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(8, 9, 10).unwrap() Utc.with_ymd_and_hms(2014, 5, 6, 8, 9, 10).unwrap()
); );
assert_eq!( assert_eq!(
dt.signed_duration_since(edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(10, 11, 12).unwrap()), dt.signed_duration_since(edt.with_ymd_and_hms(2014, 5, 6, 10, 11, 12).unwrap()),
TimeDelta::seconds(-7 * 3600 - 3 * 60 - 3) TimeDelta::seconds(-7 * 3600 - 3 * 60 - 3)
); );
assert_eq!(*Utc.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap().offset(), Utc); assert_eq!(*Utc.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), Utc);
assert_eq!(*edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap().offset(), edt); assert_eq!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset(), edt);
assert!(*edt.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap().offset() != est); assert!(*edt.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap().offset() != est);
} }
#[test] #[test]
fn test_datetime_date_and_time() { fn test_datetime_date_and_time() {
let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap(); let tz = FixedOffset::east_opt(5 * 60 * 60).unwrap();
let d = tz.ymd_opt(2014, 5, 6).unwrap().and_hms_opt(7, 8, 9).unwrap(); let d = tz.with_ymd_and_hms(2014, 5, 6, 7, 8, 9).unwrap();
assert_eq!(d.time(), NaiveTime::from_hms_opt(7, 8, 9).unwrap()); assert_eq!(d.time(), NaiveTime::from_hms_opt(7, 8, 9).unwrap());
assert_eq!(d.date(), tz.ymd_opt(2014, 5, 6).unwrap()); assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2014, 5, 6).unwrap());
assert_eq!(d.date().naive_local(), NaiveDate::from_ymd_opt(2014, 5, 6).unwrap());
assert_eq!(d.date().and_time(d.time()), Some(d));
let tz = FixedOffset::east_opt(4 * 60 * 60).unwrap(); let tz = FixedOffset::east_opt(4 * 60 * 60).unwrap();
let d = tz.ymd_opt(2016, 5, 4).unwrap().and_hms_opt(3, 2, 1).unwrap(); let d = tz.with_ymd_and_hms(2016, 5, 4, 3, 2, 1).unwrap();
assert_eq!(d.time(), NaiveTime::from_hms_opt(3, 2, 1).unwrap()); assert_eq!(d.time(), NaiveTime::from_hms_opt(3, 2, 1).unwrap());
assert_eq!(d.date(), tz.ymd_opt(2016, 5, 4).unwrap()); assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2016, 5, 4).unwrap());
assert_eq!(d.date().naive_local(), NaiveDate::from_ymd_opt(2016, 5, 4).unwrap());
assert_eq!(d.date().and_time(d.time()), Some(d));
let tz = FixedOffset::west_opt(13 * 60 * 60).unwrap(); let tz = FixedOffset::west_opt(13 * 60 * 60).unwrap();
let d = tz.ymd_opt(2017, 8, 9).unwrap().and_hms_opt(12, 34, 56).unwrap(); let d = tz.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap();
assert_eq!(d.time(), NaiveTime::from_hms_opt(12, 34, 56).unwrap()); assert_eq!(d.time(), NaiveTime::from_hms_opt(12, 34, 56).unwrap());
assert_eq!(d.date(), tz.ymd_opt(2017, 8, 9).unwrap()); assert_eq!(d.date_naive(), NaiveDate::from_ymd_opt(2017, 8, 9).unwrap());
assert_eq!(d.date().naive_local(), NaiveDate::from_ymd_opt(2017, 8, 9).unwrap());
assert_eq!(d.date().and_time(d.time()), Some(d));
let utc_d = Utc.ymd_opt(2017, 8, 9).unwrap().and_hms_opt(12, 34, 56).unwrap(); let utc_d = Utc.with_ymd_and_hms(2017, 8, 9, 12, 34, 56).unwrap();
assert!(utc_d < d); assert!(utc_d < d);
} }
@ -122,73 +116,92 @@ fn test_datetime_with_timezone() {
fn test_datetime_rfc2822_and_rfc3339() { fn test_datetime_rfc2822_and_rfc3339() {
let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap(); let edt = FixedOffset::east_opt(5 * 60 * 60).unwrap();
assert_eq!( assert_eq!(
Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap().to_rfc2822(), Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc2822(),
"Wed, 18 Feb 2015 23:16:09 +0000" "Wed, 18 Feb 2015 23:16:09 +0000"
); );
assert_eq!( assert_eq!(
Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_opt(23, 16, 9).unwrap().to_rfc3339(), Utc.with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap().to_rfc3339(),
"2015-02-18T23:16:09+00:00" "2015-02-18T23:16:09+00:00"
); );
assert_eq!( assert_eq!(
edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap().to_rfc2822(), edt.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap()
.to_rfc2822(),
"Wed, 18 Feb 2015 23:16:09 +0500" "Wed, 18 Feb 2015 23:16:09 +0500"
); );
assert_eq!( assert_eq!(
edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap().to_rfc3339(), edt.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap()
.to_rfc3339(),
"2015-02-18T23:16:09.150+05:00" "2015-02-18T23:16:09.150+05:00"
); );
assert_eq!( assert_eq!(
edt.ymd_opt(2015, 2, 18) edt.from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2015, 2, 18)
.and_hms_micro_opt(23, 59, 59, 1_234_567) .unwrap()
.unwrap() .and_hms_micro_opt(23, 59, 59, 1_234_567)
.to_rfc2822(), .unwrap()
)
.unwrap()
.to_rfc2822(),
"Wed, 18 Feb 2015 23:59:60 +0500" "Wed, 18 Feb 2015 23:59:60 +0500"
); );
assert_eq!( assert_eq!(
edt.ymd_opt(2015, 2, 18) edt.from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2015, 2, 18)
.and_hms_micro_opt(23, 59, 59, 1_234_567) .unwrap()
.unwrap() .and_hms_micro_opt(23, 59, 59, 1_234_567)
.to_rfc3339(), .unwrap()
)
.unwrap()
.to_rfc3339(),
"2015-02-18T23:59:60.234567+05:00" "2015-02-18T23:59:60.234567+05:00"
); );
assert_eq!( assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"), DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 +0000"),
Ok(FixedOffset::east_opt(0) Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap())
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_opt(23, 16, 9)
.unwrap())
); );
assert_eq!( assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"), DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:16:09 -0000"),
Ok(FixedOffset::east_opt(0) Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap())
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_opt(23, 16, 9)
.unwrap())
); );
assert_eq!( assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:16:09Z"), DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:16:09Z"),
Ok(FixedOffset::east_opt(0) Ok(FixedOffset::east_opt(0).unwrap().with_ymd_and_hms(2015, 2, 18, 23, 16, 9).unwrap())
.unwrap()
.ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_opt(23, 16, 9)
.unwrap())
); );
assert_eq!( assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"), DateTime::<FixedOffset>::parse_from_rfc2822("Wed, 18 Feb 2015 23:59:60 +0500"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap()) Ok(edt
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 59, 59, 1_000)
.unwrap()
)
.unwrap())
); );
assert!(DateTime::<FixedOffset>::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err()); assert!(DateTime::<FixedOffset>::parse_from_rfc2822("31 DEC 262143 23:59 -2359").is_err());
assert_eq!( assert_eq!(
DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"), DateTime::<FixedOffset>::parse_from_rfc3339("2015-02-18T23:59:60.234567+05:00"),
Ok(edt.ymd_opt(2015, 2, 18).unwrap().and_hms_micro_opt(23, 59, 59, 1_234_567).unwrap()) Ok(edt
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_micro_opt(23, 59, 59, 1_234_567)
.unwrap()
)
.unwrap())
); );
} }
@ -196,7 +209,14 @@ fn test_datetime_rfc2822_and_rfc3339() {
fn test_rfc3339_opts() { fn test_rfc3339_opts() {
use crate::SecondsFormat::*; use crate::SecondsFormat::*;
let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
let dt = pst.ymd_opt(2018, 1, 11).unwrap().and_hms_nano_opt(10, 5, 13, 84_660_000).unwrap(); let dt = pst
.from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 13, 84_660_000)
.unwrap(),
)
.unwrap();
assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00"); assert_eq!(dt.to_rfc3339_opts(Secs, false), "2018-01-11T10:05:13+08:00");
assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00"); assert_eq!(dt.to_rfc3339_opts(Secs, true), "2018-01-11T10:05:13+08:00");
assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00"); assert_eq!(dt.to_rfc3339_opts(Millis, false), "2018-01-11T10:05:13.084+08:00");
@ -218,7 +238,7 @@ fn test_rfc3339_opts() {
#[should_panic] #[should_panic]
fn test_rfc3339_opts_nonexhaustive() { fn test_rfc3339_opts_nonexhaustive() {
use crate::SecondsFormat; use crate::SecondsFormat;
let dt = Utc.ymd_opt(1999, 10, 9).unwrap().and_hms_opt(1, 2, 3).unwrap(); let dt = Utc.with_ymd_and_hms(1999, 10, 9, 1, 2, 3).unwrap();
dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true); dt.to_rfc3339_opts(SecondsFormat::__NonExhaustive, true);
} }
@ -228,51 +248,95 @@ fn test_datetime_from_str() {
"2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), "2015-02-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::east_opt(0) Ok(FixedOffset::east_opt(0)
.unwrap() .unwrap()
.ymd_opt(2015, 2, 18) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2015, 2, 18)
.and_hms_milli_opt(23, 16, 9, 150) .unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap()) .unwrap())
); );
assert_eq!( assert_eq!(
"2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(), "2015-02-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) Ok(Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap())
); );
assert_eq!( assert_eq!(
"2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(), "2015-02-18T23:16:9.15 UTC".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) Ok(Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap())
); );
assert_eq!( assert_eq!(
"2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(), "2015-02-18T23:16:9.15UTC".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) Ok(Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap())
); );
assert_eq!( assert_eq!(
"2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(), "2015-2-18T23:16:9.15Z".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::east_opt(0) Ok(FixedOffset::east_opt(0)
.unwrap() .unwrap()
.ymd_opt(2015, 2, 18) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2015, 2, 18)
.and_hms_milli_opt(23, 16, 9, 150) .unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap()) .unwrap())
); );
assert_eq!( assert_eq!(
"2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(), "2015-2-18T13:16:9.15-10:00".parse::<DateTime<FixedOffset>>(),
Ok(FixedOffset::west_opt(10 * 3600) Ok(FixedOffset::west_opt(10 * 3600)
.unwrap() .unwrap()
.ymd_opt(2015, 2, 18) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2015, 2, 18)
.and_hms_milli_opt(13, 16, 9, 150) .unwrap()
.and_hms_milli_opt(13, 16, 9, 150)
.unwrap()
)
.unwrap()) .unwrap())
); );
assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err()); assert!("2015-2-18T23:16:9.15".parse::<DateTime<FixedOffset>>().is_err());
assert_eq!( assert_eq!(
"2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(), "2015-2-18T23:16:9.15Z".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) Ok(Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap())
); );
assert_eq!( assert_eq!(
"2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(), "2015-2-18T13:16:9.15-10:00".parse::<DateTime<Utc>>(),
Ok(Utc.ymd_opt(2015, 2, 18).unwrap().and_hms_milli_opt(23, 16, 9, 150).unwrap()) Ok(Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2015, 2, 18)
.unwrap()
.and_hms_milli_opt(23, 16, 9, 150)
.unwrap()
)
.unwrap())
); );
assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err()); assert!("2015-2-18T23:16:9.15".parse::<DateTime<Utc>>().is_err());
@ -282,7 +346,7 @@ fn test_datetime_from_str() {
#[test] #[test]
fn test_datetime_parse_from_str() { fn test_datetime_parse_from_str() {
let ymdhms = |y, m, d, h, n, s, off| { let ymdhms = |y, m, d, h, n, s, off| {
FixedOffset::east_opt(off).unwrap().ymd_opt(y, m, d).unwrap().and_hms_opt(h, n, s).unwrap() FixedOffset::east_opt(off).unwrap().with_ymd_and_hms(y, m, d, h, n, s).unwrap()
}; };
assert_eq!( assert_eq!(
DateTime::<FixedOffset>::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), DateTime::<FixedOffset>::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"),
@ -296,13 +360,13 @@ fn test_datetime_parse_from_str() {
.is_err()); .is_err());
assert_eq!( assert_eq!(
Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"), Utc.datetime_from_str("Fri, 09 Aug 2013 23:54:35 GMT", "%a, %d %b %Y %H:%M:%S GMT"),
Ok(Utc.ymd_opt(2013, 8, 9).unwrap().and_hms_opt(23, 54, 35).unwrap()) Ok(Utc.with_ymd_and_hms(2013, 8, 9, 23, 54, 35).unwrap())
); );
} }
#[test] #[test]
fn test_to_string_round_trip() { fn test_to_string_round_trip() {
let dt = Utc.ymd_opt(2000, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); let dt = Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap();
let _dt: DateTime<Utc> = dt.to_string().parse().unwrap(); let _dt: DateTime<Utc> = dt.to_string().parse().unwrap();
let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(3600).unwrap()); let ndt_fixed = dt.with_timezone(&FixedOffset::east_opt(3600).unwrap());
@ -352,7 +416,14 @@ fn test_datetime_is_send() {
#[test] #[test]
fn test_subsecond_part() { fn test_subsecond_part() {
let datetime = Utc.ymd_opt(2014, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 1234567).unwrap(); let datetime = Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2014, 7, 8)
.unwrap()
.and_hms_nano_opt(9, 10, 11, 1234567)
.unwrap(),
)
.unwrap();
assert_eq!(1, datetime.timestamp_subsec_millis()); assert_eq!(1, datetime.timestamp_subsec_millis());
assert_eq!(1234, datetime.timestamp_subsec_micros()); assert_eq!(1234, datetime.timestamp_subsec_micros());
@ -364,31 +435,52 @@ fn test_subsecond_part() {
fn test_from_system_time() { fn test_from_system_time() {
use std::time::Duration; use std::time::Duration;
let epoch = Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap();
let nanos = 999_999_999; let nanos = 999_999_999;
// SystemTime -> DateTime<Utc> // SystemTime -> DateTime<Utc>
assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch);
assert_eq!( assert_eq!(
DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)),
Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 39, nanos).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(2001, 9, 9)
.unwrap()
.and_hms_nano_opt(1, 46, 39, nanos)
.unwrap()
)
.unwrap()
); );
assert_eq!( assert_eq!(
DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)),
Utc.ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1).unwrap()
)
.unwrap()
); );
// DateTime<Utc> -> SystemTime // DateTime<Utc> -> SystemTime
assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); assert_eq!(SystemTime::from(epoch), UNIX_EPOCH);
assert_eq!( assert_eq!(
SystemTime::from( SystemTime::from(
Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 39, nanos).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(2001, 9, 9)
.unwrap()
.and_hms_nano_opt(1, 46, 39, nanos)
.unwrap()
)
.unwrap()
), ),
UNIX_EPOCH + Duration::new(999_999_999, nanos) UNIX_EPOCH + Duration::new(999_999_999, nanos)
); );
assert_eq!( assert_eq!(
SystemTime::from( SystemTime::from(
Utc.ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(1938, 4, 24)
.unwrap()
.and_hms_nano_opt(22, 13, 20, 1)
.unwrap()
)
.unwrap()
), ),
UNIX_EPOCH - Duration::new(999_999_999, 999_999_999) UNIX_EPOCH - Duration::new(999_999_999, 999_999_999)
); );
@ -415,30 +507,54 @@ fn test_from_system_time() {
let nanos = 999_999_000; let nanos = 999_999_000;
let epoch = Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap(); let epoch = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap();
// SystemTime -> DateTime<Utc> // SystemTime -> DateTime<Utc>
assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch); assert_eq!(DateTime::<Utc>::from(UNIX_EPOCH), epoch);
assert_eq!( assert_eq!(
DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)), DateTime::<Utc>::from(UNIX_EPOCH + Duration::new(999_999_999, nanos)),
Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 39, nanos).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(2001, 9, 9)
.unwrap()
.and_hms_nano_opt(1, 46, 39, nanos)
.unwrap()
)
.unwrap()
); );
assert_eq!( assert_eq!(
DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)), DateTime::<Utc>::from(UNIX_EPOCH - Duration::new(999_999_999, nanos)),
Utc.ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1_000).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(1938, 4, 24)
.unwrap()
.and_hms_nano_opt(22, 13, 20, 1_000)
.unwrap()
)
.unwrap()
); );
// DateTime<Utc> -> SystemTime // DateTime<Utc> -> SystemTime
assert_eq!(SystemTime::from(epoch), UNIX_EPOCH); assert_eq!(SystemTime::from(epoch), UNIX_EPOCH);
assert_eq!( assert_eq!(
SystemTime::from( SystemTime::from(
Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_nano_opt(1, 46, 39, nanos).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(2001, 9, 9)
.unwrap()
.and_hms_nano_opt(1, 46, 39, nanos)
.unwrap()
)
.unwrap()
), ),
UNIX_EPOCH + Duration::new(999_999_999, nanos) UNIX_EPOCH + Duration::new(999_999_999, nanos)
); );
assert_eq!( assert_eq!(
SystemTime::from( SystemTime::from(
Utc.ymd_opt(1938, 4, 24).unwrap().and_hms_nano_opt(22, 13, 20, 1_000).unwrap() Utc.from_local_datetime(
&NaiveDate::from_ymd_opt(1938, 4, 24)
.unwrap()
.and_hms_nano_opt(22, 13, 20, 1_000)
.unwrap()
)
.unwrap()
), ),
UNIX_EPOCH - Duration::new(999_999_999, nanos) UNIX_EPOCH - Duration::new(999_999_999, nanos)
); );
@ -460,7 +576,7 @@ fn test_from_system_time() {
#[test] #[test]
fn test_datetime_format_alignment() { fn test_datetime_format_alignment() {
let datetime = Utc.ymd_opt(2007, 1, 2).unwrap(); let datetime = Utc.with_ymd_and_hms(2007, 1, 2, 0, 0, 0).unwrap();
// Item::Literal // Item::Literal
let percent = datetime.format("%%"); let percent = datetime.format("%%");
@ -517,16 +633,18 @@ fn test_years_elapsed() {
const WEEKS_PER_YEAR: f32 = 52.1775; const WEEKS_PER_YEAR: f32 = 52.1775;
// This is always at least one year because 1 year = 52.1775 weeks. // This is always at least one year because 1 year = 52.1775 weeks.
let one_year_ago = Utc::today() - TimeDelta::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64); let one_year_ago =
Utc::now().date_naive() - TimeDelta::weeks((WEEKS_PER_YEAR * 1.5).ceil() as i64);
// A bit more than 2 years. // A bit more than 2 years.
let two_year_ago = Utc::today() - TimeDelta::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64); let two_year_ago =
Utc::now().date_naive() - TimeDelta::weeks((WEEKS_PER_YEAR * 2.5).ceil() as i64);
assert_eq!(Utc::today().years_since(one_year_ago), Some(1)); assert_eq!(Utc::now().date_naive().years_since(one_year_ago), Some(1));
assert_eq!(Utc::today().years_since(two_year_ago), Some(2)); assert_eq!(Utc::now().date_naive().years_since(two_year_ago), Some(2));
// If the given DateTime is later than now, the function will always return 0. // If the given DateTime is later than now, the function will always return 0.
let future = Utc::today() + TimeDelta::weeks(12); let future = Utc::now().date_naive() + TimeDelta::weeks(12);
assert_eq!(Utc::today().years_since(future), None); assert_eq!(Utc::now().date_naive().years_since(future), None);
} }
#[test] #[test]

View File

@ -20,7 +20,7 @@
//! # use std::error::Error; //! # use std::error::Error;
//! use chrono::prelude::*; //! use chrono::prelude::*;
//! //!
//! let date_time = Utc.ymd_opt(2020, 11, 10).unwrap().and_hms_opt(0, 1, 32).unwrap(); //! let date_time = Utc.with_ymd_and_hms(2020, 11, 10, 0, 1, 32).unwrap();
//! //!
//! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S")); //! let formatted = format!("{}", date_time.format("%Y-%m-%d %H:%M:%S"));
//! assert_eq!(formatted, "2020-11-10 00:01:32"); //! assert_eq!(formatted, "2020-11-10 00:01:32");
@ -40,6 +40,7 @@ use alloc::string::{String, ToString};
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow; use core::borrow::Borrow;
use core::fmt; use core::fmt;
use core::fmt::Write;
use core::str::FromStr; use core::str::FromStr;
#[cfg(any(feature = "std", test))] #[cfg(any(feature = "std", test))]
use std::error::Error; use std::error::Error;
@ -426,6 +427,63 @@ const TOO_SHORT: ParseError = ParseError(ParseErrorKind::TooShort);
const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong); const TOO_LONG: ParseError = ParseError(ParseErrorKind::TooLong);
const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat); const BAD_FORMAT: ParseError = ParseError(ParseErrorKind::BadFormat);
#[cfg(any(feature = "alloc", feature = "std", test))]
struct Locales {
short_months: &'static [&'static str],
long_months: &'static [&'static str],
short_weekdays: &'static [&'static str],
long_weekdays: &'static [&'static str],
am_pm: &'static [&'static str],
}
#[cfg(any(feature = "alloc", feature = "std", test))]
impl Locales {
fn new(_locale: Option<Locale>) -> Self {
#[cfg(feature = "unstable-locales")]
{
let locale = _locale.unwrap_or(Locale::POSIX);
Self {
short_months: locales::short_months(locale),
long_months: locales::long_months(locale),
short_weekdays: locales::short_weekdays(locale),
long_weekdays: locales::long_weekdays(locale),
am_pm: locales::am_pm(locale),
}
}
#[cfg(not(feature = "unstable-locales"))]
Self {
short_months: &[
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
],
long_months: &[
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
short_weekdays: &["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
long_weekdays: &[
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
],
am_pm: &["AM", "PM"],
}
}
}
/// Formats single formatting item /// Formats single formatting item
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
@ -448,47 +506,10 @@ fn format_inner<'a>(
time: Option<&NaiveTime>, time: Option<&NaiveTime>,
off: Option<&(String, FixedOffset)>, off: Option<&(String, FixedOffset)>,
item: &Item<'a>, item: &Item<'a>,
_locale: Option<Locale>, locale: Option<Locale>,
) -> fmt::Result { ) -> fmt::Result {
#[cfg(feature = "unstable-locales")] let locale = Locales::new(locale);
let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
let locale = _locale.unwrap_or(Locale::POSIX);
let am_pm = locales::am_pm(locale);
(
locales::short_months(locale),
locales::long_months(locale),
locales::short_weekdays(locale),
locales::long_weekdays(locale),
am_pm,
&[am_pm[0].to_lowercase(), am_pm[1].to_lowercase()],
)
};
#[cfg(not(feature = "unstable-locales"))]
let (short_months, long_months, short_weekdays, long_weekdays, am_pm, am_pm_lowercase) = {
(
&["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
&[
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
&["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
&["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
&["AM", "PM"],
&["am", "pm"],
)
};
use core::fmt::Write;
use num_integer::{div_floor, mod_floor}; use num_integer::{div_floor, mod_floor};
match *item { match *item {
@ -564,77 +585,41 @@ fn format_inner<'a>(
Item::Fixed(ref spec) => { Item::Fixed(ref spec) => {
use self::Fixed::*; use self::Fixed::*;
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
fn write_local_minus_utc(
result: &mut String,
off: FixedOffset,
allow_zulu: bool,
colon_type: Colons,
) -> fmt::Result {
let off = off.local_minus_utc();
if !allow_zulu || off != 0 {
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
match colon_type {
Colons::None => {
write!(result, "{}{:02}{:02}", sign, off / 3600, off / 60 % 60)
}
Colons::Single => {
write!(result, "{}{:02}:{:02}", sign, off / 3600, off / 60 % 60)
}
Colons::Double => {
write!(
result,
"{}{:02}:{:02}:{:02}",
sign,
off / 3600,
off / 60 % 60,
off % 60
)
}
Colons::Triple => {
write!(result, "{}{:02}", sign, off / 3600)
}
}
} else {
result.push('Z');
Ok(())
}
}
let ret = let ret =
match *spec { match *spec {
ShortMonthName => date.map(|d| { ShortMonthName => date.map(|d| {
result.push_str(short_months[d.month0() as usize]); result.push_str(locale.short_months[d.month0() as usize]);
Ok(()) Ok(())
}), }),
LongMonthName => date.map(|d| { LongMonthName => date.map(|d| {
result.push_str(long_months[d.month0() as usize]); result.push_str(locale.long_months[d.month0() as usize]);
Ok(()) Ok(())
}), }),
ShortWeekdayName => date.map(|d| { ShortWeekdayName => date.map(|d| {
result result.push_str(
.push_str(short_weekdays[d.weekday().num_days_from_sunday() as usize]); locale.short_weekdays[d.weekday().num_days_from_sunday() as usize],
);
Ok(()) Ok(())
}), }),
LongWeekdayName => date.map(|d| { LongWeekdayName => date.map(|d| {
result.push_str(long_weekdays[d.weekday().num_days_from_sunday() as usize]); result.push_str(
locale.long_weekdays[d.weekday().num_days_from_sunday() as usize],
);
Ok(()) Ok(())
}), }),
LowerAmPm => time.map(|t| { LowerAmPm => time.map(|t| {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::useless_asref))] let ampm = if t.hour12().0 { locale.am_pm[1] } else { locale.am_pm[0] };
{ for char in ampm.chars() {
result.push_str(if t.hour12().0 { result.extend(char.to_lowercase())
am_pm_lowercase[1].as_ref()
} else {
am_pm_lowercase[0].as_ref()
});
} }
Ok(()) Ok(())
}), }),
UpperAmPm => time.map(|t| { UpperAmPm => time.map(|t| {
result.push_str(if t.hour12().0 { am_pm[1] } else { am_pm[0] }); result.push_str(if t.hour12().0 {
locale.am_pm[1]
} else {
locale.am_pm[0]
});
Ok(()) Ok(())
}), }),
Nanosecond => time.map(|t| { Nanosecond => time.map(|t| {
@ -701,19 +686,7 @@ fn format_inner<'a>(
// same as `%a, %d %b %Y %H:%M:%S %z` // same as `%a, %d %b %Y %H:%M:%S %z`
{ {
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
let sec = t.second() + t.nanosecond() / 1_000_000_000; Some(write_rfc2822_inner(result, d, t, off, locale))
write!(
result,
"{}, {:02} {} {:04} {:02}:{:02}:{:02} ",
short_weekdays[d.weekday().num_days_from_sunday() as usize],
d.day(),
short_months[d.month0() as usize],
d.year(),
t.hour(),
t.minute(),
sec
)?;
Some(write_local_minus_utc(result, off, false, Colons::None))
} else { } else {
None None
} }
@ -722,10 +695,7 @@ fn format_inner<'a>(
// same as `%Y-%m-%dT%H:%M:%S%.f%:z` // same as `%Y-%m-%dT%H:%M:%S%.f%:z`
{ {
if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) { if let (Some(d), Some(t), Some(&(_, off))) = (date, time, off) {
// reuse `Debug` impls which already print ISO 8601 format. Some(write_rfc3339(result, crate::NaiveDateTime::new(*d, *t), off))
// this is faster in this way.
write!(result, "{:?}T{:?}", d, t)?;
Some(write_local_minus_utc(result, off, false, Colons::Single))
} else { } else {
None None
} }
@ -743,6 +713,110 @@ fn format_inner<'a>(
Ok(()) Ok(())
} }
/// Prints an offset from UTC in the format of `+HHMM` or `+HH:MM`.
/// `Z` instead of `+00[:]00` is allowed when `allow_zulu` is true.
#[cfg(any(feature = "alloc", feature = "std", test))]
fn write_local_minus_utc(
result: &mut String,
off: FixedOffset,
allow_zulu: bool,
colon_type: Colons,
) -> fmt::Result {
let off = off.local_minus_utc();
if allow_zulu && off == 0 {
result.push('Z');
return Ok(());
}
let (sign, off) = if off < 0 { ('-', -off) } else { ('+', off) };
result.push(sign);
write_hundreds(result, (off / 3600) as u8)?;
match colon_type {
Colons::None => write_hundreds(result, (off / 60 % 60) as u8),
Colons::Single => {
result.push(':');
write_hundreds(result, (off / 60 % 60) as u8)
}
Colons::Double => {
result.push(':');
write_hundreds(result, (off / 60 % 60) as u8)?;
result.push(':');
write_hundreds(result, (off % 60) as u8)
}
Colons::Triple => Ok(()),
}
}
/// Writes the date, time and offset to the string. same as `%Y-%m-%dT%H:%M:%S%.f%:z`
#[cfg(any(feature = "alloc", feature = "std", test))]
pub(crate) fn write_rfc3339(
result: &mut String,
dt: crate::NaiveDateTime,
off: FixedOffset,
) -> fmt::Result {
// reuse `Debug` impls which already print ISO 8601 format.
// this is faster in this way.
write!(result, "{:?}", dt)?;
write_local_minus_utc(result, off, false, Colons::Single)
}
#[cfg(any(feature = "alloc", feature = "std", test))]
/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z`
pub(crate) fn write_rfc2822(
result: &mut String,
dt: crate::NaiveDateTime,
off: FixedOffset,
) -> fmt::Result {
write_rfc2822_inner(result, &dt.date(), &dt.time(), off, Locales::new(None))
}
#[cfg(any(feature = "alloc", feature = "std", test))]
/// write datetimes like `Tue, 1 Jul 2003 10:52:37 +0200`, same as `%a, %d %b %Y %H:%M:%S %z`
fn write_rfc2822_inner(
result: &mut String,
d: &NaiveDate,
t: &NaiveTime,
off: FixedOffset,
locale: Locales,
) -> fmt::Result {
let year = d.year();
// RFC2822 is only defined on years 0 through 9999
if !(0..=9999).contains(&year) {
return Err(fmt::Error);
}
result.push_str(locale.short_weekdays[d.weekday().num_days_from_sunday() as usize]);
result.push_str(", ");
write_hundreds(result, d.day() as u8)?;
result.push(' ');
result.push_str(locale.short_months[d.month0() as usize]);
result.push(' ');
write_hundreds(result, (year / 100) as u8)?;
write_hundreds(result, (year % 100) as u8)?;
result.push(' ');
write_hundreds(result, t.hour() as u8)?;
result.push(':');
write_hundreds(result, t.minute() as u8)?;
result.push(':');
let sec = t.second() + t.nanosecond() / 1_000_000_000;
write_hundreds(result, sec as u8)?;
result.push(' ');
write_local_minus_utc(result, off, false, Colons::None)
}
/// Equivalent to `{:02}` formatting for n < 100.
pub(crate) fn write_hundreds(w: &mut impl Write, n: u8) -> fmt::Result {
if n >= 100 {
return Err(fmt::Error);
}
let tens = b'0' + n / 10;
let ones = b'0' + n % 10;
w.write_char(tens as char)?;
w.write_char(ones as char)
}
/// Tries to format given arguments with given formatting items. /// Tries to format given arguments with given formatting items.
/// Internally used by `DelayedFormat`. /// Internally used by `DelayedFormat`.
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]

View File

@ -892,7 +892,7 @@ fn parse_rfc850() {
static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT"; static RFC850_FMT: &str = "%A, %d-%b-%y %T GMT";
let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT"; let dt_str = "Sunday, 06-Nov-94 08:49:37 GMT";
let dt = Utc.ymd_opt(1994, 11, 6).unwrap().and_hms_opt(8, 49, 37).unwrap(); let dt = Utc.with_ymd_and_hms(1994, 11, 6, 8, 49, 37).unwrap();
// Check that the format is what we expect // Check that the format is what we expect
assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str); assert_eq!(dt.format(RFC850_FMT).to_string(), dt_str);
@ -903,28 +903,19 @@ fn parse_rfc850() {
// Check that the rest of the weekdays parse correctly (this test originally failed because // Check that the rest of the weekdays parse correctly (this test originally failed because
// Sunday parsed incorrectly). // Sunday parsed incorrectly).
let testdates = [ let testdates = [
(Utc.with_ymd_and_hms(1994, 11, 7, 8, 49, 37).unwrap(), "Monday, 07-Nov-94 08:49:37 GMT"),
(Utc.with_ymd_and_hms(1994, 11, 8, 8, 49, 37).unwrap(), "Tuesday, 08-Nov-94 08:49:37 GMT"),
( (
Utc.ymd_opt(1994, 11, 7).unwrap().and_hms_opt(8, 49, 37).unwrap(), Utc.with_ymd_and_hms(1994, 11, 9, 8, 49, 37).unwrap(),
"Monday, 07-Nov-94 08:49:37 GMT",
),
(
Utc.ymd_opt(1994, 11, 8).unwrap().and_hms_opt(8, 49, 37).unwrap(),
"Tuesday, 08-Nov-94 08:49:37 GMT",
),
(
Utc.ymd_opt(1994, 11, 9).unwrap().and_hms_opt(8, 49, 37).unwrap(),
"Wednesday, 09-Nov-94 08:49:37 GMT", "Wednesday, 09-Nov-94 08:49:37 GMT",
), ),
( (
Utc.ymd_opt(1994, 11, 10).unwrap().and_hms_opt(8, 49, 37).unwrap(), Utc.with_ymd_and_hms(1994, 11, 10, 8, 49, 37).unwrap(),
"Thursday, 10-Nov-94 08:49:37 GMT", "Thursday, 10-Nov-94 08:49:37 GMT",
), ),
(Utc.with_ymd_and_hms(1994, 11, 11, 8, 49, 37).unwrap(), "Friday, 11-Nov-94 08:49:37 GMT"),
( (
Utc.ymd_opt(1994, 11, 11).unwrap().and_hms_opt(8, 49, 37).unwrap(), Utc.with_ymd_and_hms(1994, 11, 12, 8, 49, 37).unwrap(),
"Friday, 11-Nov-94 08:49:37 GMT",
),
(
Utc.ymd_opt(1994, 11, 12).unwrap().and_hms_opt(8, 49, 37).unwrap(),
"Saturday, 12-Nov-94 08:49:37 GMT", "Saturday, 12-Nov-94 08:49:37 GMT",
), ),
]; ];

View File

@ -229,7 +229,7 @@ impl Parsed {
/// (`false` for AM, `true` for PM) /// (`false` for AM, `true` for PM)
#[inline] #[inline]
pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> { pub fn set_ampm(&mut self, value: bool) -> ParseResult<()> {
set_if_consistent(&mut self.hour_div_12, if value { 1 } else { 0 }) set_if_consistent(&mut self.hour_div_12, u32::from(value))
} }
/// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from /// Tries to set the [`hour_mod_12`](#structfield.hour_mod_12) field from
@ -1185,9 +1185,12 @@ mod tests {
let ymdhmsn = |y, m, d, h, n, s, nano, off| { let ymdhmsn = |y, m, d, h, n, s, nano, off| {
Ok(FixedOffset::east_opt(off) Ok(FixedOffset::east_opt(off)
.unwrap() .unwrap()
.ymd_opt(y, m, d) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(y, m, d)
.and_hms_nano_opt(h, n, s, nano) .unwrap()
.and_hms_nano_opt(h, n, s, nano)
.unwrap(),
)
.unwrap()) .unwrap())
}; };
@ -1232,7 +1235,14 @@ mod tests {
parse!(Utc; parse!(Utc;
year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4, year: 2014, ordinal: 365, hour_div_12: 0, hour_mod_12: 4,
minute: 26, second: 40, nanosecond: 12_345_678, offset: 0), minute: 26, second: 40, nanosecond: 12_345_678, offset: 0),
Ok(Utc.ymd_opt(2014, 12, 31).unwrap().and_hms_nano_opt(4, 26, 40, 12_345_678).unwrap()) Ok(Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2014, 12, 31)
.unwrap()
.and_hms_nano_opt(4, 26, 40, 12_345_678)
.unwrap()
)
.unwrap())
); );
assert_eq!( assert_eq!(
parse!(Utc; parse!(Utc;
@ -1252,16 +1262,19 @@ mod tests {
minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400), minute: 26, second: 40, nanosecond: 12_345_678, offset: 32400),
Ok(FixedOffset::east_opt(32400) Ok(FixedOffset::east_opt(32400)
.unwrap() .unwrap()
.ymd_opt(2014, 12, 31) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2014, 12, 31)
.and_hms_nano_opt(13, 26, 40, 12_345_678) .unwrap()
.and_hms_nano_opt(13, 26, 40, 12_345_678)
.unwrap()
)
.unwrap()) .unwrap())
); );
// single result from timestamp // single result from timestamp
assert_eq!( assert_eq!(
parse!(Utc; timestamp: 1_420_000_000, offset: 0), parse!(Utc; timestamp: 1_420_000_000, offset: 0),
Ok(Utc.ymd_opt(2014, 12, 31).unwrap().and_hms_opt(4, 26, 40).unwrap()) Ok(Utc.with_ymd_and_hms(2014, 12, 31, 4, 26, 40).unwrap())
); );
assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE)); assert_eq!(parse!(Utc; timestamp: 1_420_000_000, offset: 32400), Err(IMPOSSIBLE));
assert_eq!( assert_eq!(
@ -1272,9 +1285,7 @@ mod tests {
parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400), parse!(FixedOffset::east_opt(32400).unwrap(); timestamp: 1_420_000_000, offset: 32400),
Ok(FixedOffset::east_opt(32400) Ok(FixedOffset::east_opt(32400)
.unwrap() .unwrap()
.ymd_opt(2014, 12, 31) .with_ymd_and_hms(2014, 12, 31, 13, 26, 40)
.unwrap()
.and_hms_opt(13, 26, 40)
.unwrap()) .unwrap())
); );

View File

@ -556,13 +556,17 @@ fn test_strftime_items() {
#[cfg(test)] #[cfg(test)]
#[test] #[test]
fn test_strftime_docs() { fn test_strftime_docs() {
use crate::NaiveDate;
use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc}; use crate::{DateTime, FixedOffset, TimeZone, Timelike, Utc};
let dt = FixedOffset::east_opt(34200) let dt = FixedOffset::east_opt(34200)
.unwrap() .unwrap()
.ymd_opt(2001, 7, 8) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2001, 7, 8)
.and_hms_nano_opt(0, 34, 59, 1_026_490_708) .unwrap()
.and_hms_nano_opt(0, 34, 59, 1_026_490_708)
.unwrap(),
)
.unwrap(); .unwrap();
// date specifiers // date specifiers
@ -659,13 +663,12 @@ fn test_strftime_docs() {
#[cfg(feature = "unstable-locales")] #[cfg(feature = "unstable-locales")]
#[test] #[test]
fn test_strftime_docs_localized() { fn test_strftime_docs_localized() {
use crate::{FixedOffset, TimeZone}; use crate::{FixedOffset, NaiveDate};
let dt = FixedOffset::east_opt(34200) 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() .unwrap()
.ymd_opt(2001, 7, 8) .and_local_timezone(FixedOffset::east_opt(34200).unwrap())
.unwrap()
.and_hms_nano_opt(0, 34, 59, 1_026_490_708)
.unwrap(); .unwrap();
// date specifiers // date specifiers

View File

@ -112,26 +112,26 @@
//! use chrono::prelude::*; //! use chrono::prelude::*;
//! use chrono::offset::LocalResult; //! use chrono::offset::LocalResult;
//! //!
//! let dt = Utc.ymd_opt(2014, 7, 8).unwrap().and_hms_opt(9, 10, 11).unwrap(); // `2014-07-08T09:10:11Z` //! let dt = Utc.with_ymd_and_hms(2014, 7, 8, 9, 10, 11).unwrap(); // `2014-07-08T09:10:11Z`
//! // July 8 is 188th day of the year 2014 (`o` for "ordinal") //! // July 8 is 188th day of the year 2014 (`o` for "ordinal")
//! assert_eq!(dt, Utc.yo(2014, 189).and_hms_opt(9, 10, 11).unwrap()); //! assert_eq!(dt, Utc.yo(2014, 189).and_hms_opt(9, 10, 11).unwrap());
//! // July 8 is Tuesday in ISO week 28 of the year 2014. //! // July 8 is Tuesday in ISO week 28 of the year 2014.
//! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms_opt(9, 10, 11).unwrap()); //! assert_eq!(dt, Utc.isoywd(2014, 28, Weekday::Tue).and_hms_opt(9, 10, 11).unwrap());
//! //!
//! let dt = Utc.ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap(); // `2014-07-08T09:10:11.012Z` //! let dt = NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap().and_local_timezone(Utc).unwrap(); // `2014-07-08T09:10:11.012Z`
//! assert_eq!(dt, Utc.ymd_opt(2014, 7, 8).unwrap().and_hms_micro_opt(9, 10, 11, 12_000).unwrap()); //! assert_eq!(dt, NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_micro_opt(9, 10, 11, 12_000).unwrap().and_local_timezone(Utc).unwrap());
//! assert_eq!(dt, Utc.ymd_opt(2014, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 12_000_000).unwrap()); //! assert_eq!(dt, NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_nano_opt(9, 10, 11, 12_000_000).unwrap().and_local_timezone(Utc).unwrap());
//! //!
//! // dynamic verification //! // dynamic verification
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33), //! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(21, 15, 33),
//! LocalResult::Single(Utc.ymd_opt(2014, 7, 8).unwrap().and_hms_opt(21, 15, 33).unwrap())); //! LocalResult::Single(Utc.with_ymd_and_hms(2014, 7, 8, 21, 15, 33).unwrap()));
//! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None); //! assert_eq!(Utc.ymd_opt(2014, 7, 8).and_hms_opt(80, 15, 33), LocalResult::None);
//! assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None); //! assert_eq!(Utc.ymd_opt(2014, 7, 38).and_hms_opt(21, 15, 33), LocalResult::None);
//! //!
//! // other time zone objects can be used to construct a local datetime. //! // other time zone objects can be used to construct a local datetime.
//! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical. //! // obviously, `local_dt` is normally different from `dt`, but `fixed_dt` should be identical.
//! let local_dt = Local.ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap(); //! let local_dt = Local.from_local_datetime(&NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(9, 10, 11, 12).unwrap()).unwrap();
//! let fixed_dt = FixedOffset::east_opt(9 * 3600).unwrap().ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(18, 10, 11, 12).unwrap(); //! let fixed_dt = FixedOffset::east_opt(9 * 3600).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(2014, 7, 8).unwrap().and_hms_milli_opt(18, 10, 11, 12).unwrap()).unwrap();
//! assert_eq!(dt, fixed_dt); //! assert_eq!(dt, fixed_dt);
//! # let _ = local_dt; //! # let _ = local_dt;
//! ``` //! ```
@ -147,7 +147,7 @@
//! use chrono::TimeDelta; //! use chrono::TimeDelta;
//! //!
//! // assume this returned `2014-11-28T21:45:59.324310806+09:00`: //! // assume this returned `2014-11-28T21:45:59.324310806+09:00`:
//! let dt = FixedOffset::east_opt(9*3600).unwrap().ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(21, 45, 59, 324310806).unwrap(); //! let dt = FixedOffset::east_opt(9*3600).unwrap().from_local_datetime(&NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(21, 45, 59, 324310806).unwrap()).unwrap();
//! //!
//! // property accessors //! // property accessors
//! assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28)); //! assert_eq!((dt.year(), dt.month(), dt.day()), (2014, 11, 28));
@ -161,7 +161,7 @@
//! // time zone accessor and manipulation //! // time zone accessor and manipulation
//! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600); //! assert_eq!(dt.offset().fix().local_minus_utc(), 9 * 3600);
//! assert_eq!(dt.timezone(), FixedOffset::east_opt(9 * 3600).unwrap()); //! assert_eq!(dt.timezone(), FixedOffset::east_opt(9 * 3600).unwrap());
//! assert_eq!(dt.with_timezone(&Utc), Utc.ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(12, 45, 59, 324310806).unwrap()); //! assert_eq!(dt.with_timezone(&Utc), NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(12, 45, 59, 324310806).unwrap().and_local_timezone(Utc).unwrap());
//! //!
//! // a sample of property manipulations (validates dynamically) //! // a sample of property manipulations (validates dynamically)
//! assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday //! assert_eq!(dt.with_day(29).unwrap().weekday(), Weekday::Sat); // 2014-11-29 is Saturday
@ -169,14 +169,14 @@
//! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE //! assert_eq!(dt.with_year(-300).unwrap().num_days_from_ce(), -109606); // November 29, 301 BCE
//! //!
//! // arithmetic operations //! // arithmetic operations
//! let dt1 = Utc.ymd_opt(2014, 11, 14).unwrap().and_hms_opt(8, 9, 10).unwrap(); //! let dt1 = Utc.with_ymd_and_hms(2014, 11, 14, 8, 9, 10).unwrap();
//! let dt2 = Utc.ymd_opt(2014, 11, 14).unwrap().and_hms_opt(10, 9, 8).unwrap(); //! let dt2 = Utc.with_ymd_and_hms(2014, 11, 14, 10, 9, 8).unwrap();
//! assert_eq!(dt1.signed_duration_since(dt2), TimeDelta::seconds(-2 * 3600 + 2)); //! assert_eq!(dt1.signed_duration_since(dt2), TimeDelta::seconds(-2 * 3600 + 2));
//! assert_eq!(dt2.signed_duration_since(dt1), TimeDelta::seconds(2 * 3600 - 2)); //! assert_eq!(dt2.signed_duration_since(dt1), TimeDelta::seconds(2 * 3600 - 2));
//! assert_eq!(Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap() + TimeDelta::seconds(1_000_000_000), //! assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() + TimeDelta::seconds(1_000_000_000),
//! Utc.ymd_opt(2001, 9, 9).unwrap().and_hms_opt(1, 46, 40).unwrap()); //! Utc.with_ymd_and_hms(2001, 9, 9, 1, 46, 40).unwrap());
//! assert_eq!(Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 0, 0).unwrap() - TimeDelta::seconds(1_000_000_000), //! assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap() - TimeDelta::seconds(1_000_000_000),
//! Utc.ymd_opt(1938, 4, 24).unwrap().and_hms_opt(22, 13, 20).unwrap()); //! Utc.with_ymd_and_hms(1938, 4, 24, 22, 13, 20).unwrap());
//! ``` //! ```
//! //!
//! ### Formatting and Parsing //! ### Formatting and Parsing
@ -207,7 +207,7 @@
//! //!
//! # #[cfg(feature = "unstable-locales")] //! # #[cfg(feature = "unstable-locales")]
//! # fn test() { //! # fn test() {
//! let dt = Utc.ymd_opt(2014, 11, 28).unwrap().and_hms_opt(12, 0, 9).unwrap(); //! let dt = Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap();
//! assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09"); //! assert_eq!(dt.format("%Y-%m-%d %H:%M:%S").to_string(), "2014-11-28 12:00:09");
//! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014"); //! assert_eq!(dt.format("%a %b %e %T %Y").to_string(), "Fri Nov 28 12:00:09 2014");
//! assert_eq!(dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), "vendredi 28 novembre 2014, 12:00:09"); //! assert_eq!(dt.format_localized("%A %e %B %Y, %T", Locale::fr_BE).to_string(), "vendredi 28 novembre 2014, 12:00:09");
@ -219,7 +219,7 @@
//! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z"); //! assert_eq!(format!("{:?}", dt), "2014-11-28T12:00:09Z");
//! //!
//! // Note that milli/nanoseconds are only printed if they are non-zero //! // Note that milli/nanoseconds are only printed if they are non-zero
//! let dt_nano = Utc.ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(12, 0, 9, 1).unwrap(); //! let dt_nano = NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_nano_opt(12, 0, 9, 1).unwrap().and_local_timezone(Utc).unwrap();
//! assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z"); //! assert_eq!(format!("{:?}", dt_nano), "2014-11-28T12:00:09.000000001Z");
//! # } //! # }
//! # #[cfg(not(feature = "unstable-locales"))] //! # #[cfg(not(feature = "unstable-locales"))]
@ -259,7 +259,7 @@
//! ```rust //! ```rust
//! use chrono::prelude::*; //! use chrono::prelude::*;
//! //!
//! let dt = Utc.ymd_opt(2014, 11, 28).unwrap().and_hms_opt(12, 0, 9).unwrap(); //! let dt = Utc.with_ymd_and_hms(2014, 11, 28, 12, 0, 9).unwrap();
//! let fixed_dt = dt.with_timezone(&FixedOffset::east_opt(9*3600).unwrap()); //! let fixed_dt = dt.with_timezone(&FixedOffset::east_opt(9*3600).unwrap());
//! //!
//! // method 1 //! // method 1
@ -329,7 +329,7 @@
//! //!
//! assert_eq!(Utc.ymd_opt(2014, 11, 28).unwrap().weekday(), Weekday::Fri); //! assert_eq!(Utc.ymd_opt(2014, 11, 28).unwrap().weekday(), Weekday::Fri);
//! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None); //! assert_eq!(Utc.ymd_opt(2014, 11, 31), LocalResult::None);
//! assert_eq!(Utc.ymd_opt(2014, 11, 28).unwrap().and_hms_milli_opt(7, 8, 9, 10).unwrap().format("%H%M%S").to_string(), //! assert_eq!(NaiveDate::from_ymd_opt(2014, 11, 28).unwrap().and_hms_milli_opt(7, 8, 9, 10).unwrap().and_local_timezone(Utc).unwrap().format("%H%M%S").to_string(),
//! "070809"); //! "070809");
//! ``` //! ```
//! //!
@ -409,6 +409,7 @@ doctest!("../README.md");
/// A convenience module appropriate for glob imports (`use chrono::prelude::*;`). /// A convenience module appropriate for glob imports (`use chrono::prelude::*;`).
pub mod prelude { pub mod prelude {
#[doc(no_inline)] #[doc(no_inline)]
#[allow(deprecated)]
pub use crate::Date; pub use crate::Date;
#[cfg(feature = "clock")] #[cfg(feature = "clock")]
#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] #[cfg_attr(docsrs, doc(cfg(feature = "clock")))]

View File

@ -14,7 +14,7 @@ use crate::OutOfRange;
/// ``` /// ```
/// # use std::convert::TryFrom; /// # use std::convert::TryFrom;
/// use chrono::prelude::*; /// use chrono::prelude::*;
/// let date = Utc.ymd_opt(2019, 10, 28).unwrap().and_hms_opt(9, 10, 11).unwrap(); /// let date = Utc.with_ymd_and_hms(2019, 10, 28, 9, 10, 11).unwrap();
/// // `2019-10-28T09:10:11Z` /// // `2019-10-28T09:10:11Z`
/// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok(); /// let month = Month::try_from(u8::try_from(date.month()).unwrap()).ok();
/// assert_eq!(month, Some(Month::October)) /// assert_eq!(month, Some(Month::October))
@ -23,7 +23,7 @@ use crate::OutOfRange;
/// ``` /// ```
/// # use chrono::prelude::*; /// # use chrono::prelude::*;
/// let month = Month::January; /// let month = Month::January;
/// let dt = Utc.ymd_opt(2019, month.number_from_month(), 28).unwrap().and_hms_opt(9, 10, 11).unwrap(); /// 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)); /// assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
/// ``` /// ```
/// Allows mapping from and to month, from 1-January to 12-December. /// Allows mapping from and to month, from 1-January to 12-December.
@ -31,6 +31,7 @@ use crate::OutOfRange;
// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior. // Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior.
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] #[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum Month { pub enum Month {
/// January /// January
January = 0, January = 0,
@ -177,6 +178,7 @@ impl TryFrom<u8> for Month {
/// A duration in calendar months /// A duration in calendar months
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Months(pub(crate) u32); pub struct Months(pub(crate) u32);
impl Months { impl Months {
@ -317,15 +319,11 @@ mod tests {
assert_eq!(Month::try_from(12), Ok(Month::December)); assert_eq!(Month::try_from(12), Ok(Month::December));
assert_eq!(Month::try_from(13), Err(OutOfRange::new())); assert_eq!(Month::try_from(13), Err(OutOfRange::new()));
let date = Utc.ymd_opt(2019, 10, 28).unwrap().and_hms_opt(9, 10, 11).unwrap(); 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)); assert_eq!(Month::try_from(date.month() as u8).ok(), Some(Month::October));
let month = Month::January; let month = Month::January;
let dt = Utc let dt = Utc.with_ymd_and_hms(2019, month.number_from_month(), 28, 9, 10, 11).unwrap();
.ymd_opt(2019, month.number_from_month(), 28)
.unwrap()
.and_hms_opt(9, 10, 11)
.unwrap();
assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28)); assert_eq!((dt.year(), dt.month(), dt.day()), (2019, 1, 28));
} }

View File

@ -15,7 +15,7 @@ use rkyv::{Archive, Deserialize, Serialize};
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use crate::format::DelayedFormat; use crate::format::DelayedFormat;
use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems};
use crate::format::{Item, Numeric, Pad}; use crate::format::{Item, Numeric, Pad};
use crate::month::Months; use crate::month::Months;
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime}; use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
@ -191,6 +191,16 @@ pub const MIN_DATE: NaiveDate = NaiveDate::MIN;
#[deprecated(since = "0.4.20", note = "Use NaiveDate::MAX instead")] #[deprecated(since = "0.4.20", note = "Use NaiveDate::MAX instead")]
pub const MAX_DATE: NaiveDate = NaiveDate::MAX; pub const MAX_DATE: NaiveDate = NaiveDate::MAX;
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for NaiveDate {
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<NaiveDate> {
let year = u.int_in_range(MIN_YEAR..=MAX_YEAR)?;
let max_days = YearFlags::from_year(year).ndays();
let ord = u.int_in_range(1..=max_days)?;
NaiveDate::from_yo_opt(year, ord).ok_or(arbitrary::Error::IncorrectFormat)
}
}
// as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`, // as it is hard to verify year flags in `NaiveDate::MIN` and `NaiveDate::MAX`,
// we use a separate run-time test. // we use a separate run-time test.
#[test] #[test]
@ -265,7 +275,7 @@ impl NaiveDate {
/// ``` /// ```
pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<NaiveDate> { pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<NaiveDate> {
let flags = YearFlags::from_year(year); let flags = YearFlags::from_year(year);
NaiveDate::from_mdf(year, Mdf::new(month, day, flags)) NaiveDate::from_mdf(year, Mdf::new(month, day, flags)?)
} }
/// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date)
@ -299,7 +309,7 @@ impl NaiveDate {
/// ``` /// ```
pub fn from_yo_opt(year: i32, ordinal: u32) -> Option<NaiveDate> { pub fn from_yo_opt(year: i32, ordinal: u32) -> Option<NaiveDate> {
let flags = YearFlags::from_year(year); let flags = YearFlags::from_year(year);
NaiveDate::from_of(year, Of::new(ordinal, flags)) NaiveDate::from_of(year, Of::new(ordinal, flags)?)
} }
/// Makes a new `NaiveDate` from the [ISO week date](#week-date) /// Makes a new `NaiveDate` from the [ISO week date](#week-date)
@ -368,18 +378,18 @@ impl NaiveDate {
let prevflags = YearFlags::from_year(year - 1); let prevflags = YearFlags::from_year(year - 1);
NaiveDate::from_of( NaiveDate::from_of(
year - 1, year - 1,
Of::new(weekord + prevflags.ndays() - delta, prevflags), Of::new(weekord + prevflags.ndays() - delta, prevflags)?,
) )
} else { } else {
let ordinal = weekord - delta; let ordinal = weekord - delta;
let ndays = flags.ndays(); let ndays = flags.ndays();
if ordinal <= ndays { if ordinal <= ndays {
// this year // this year
NaiveDate::from_of(year, Of::new(ordinal, flags)) NaiveDate::from_of(year, Of::new(ordinal, flags)?)
} else { } else {
// ordinal > ndays, next year // ordinal > ndays, next year
let nextflags = YearFlags::from_year(year + 1); let nextflags = YearFlags::from_year(year + 1);
NaiveDate::from_of(year + 1, Of::new(ordinal - ndays, nextflags)) NaiveDate::from_of(year + 1, Of::new(ordinal - ndays, nextflags)?)
} }
} }
} else { } else {
@ -422,7 +432,7 @@ impl NaiveDate {
let (year_div_400, cycle) = div_mod_floor(days, 146_097); let (year_div_400, cycle) = div_mod_floor(days, 146_097);
let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)?)
} }
/// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week
@ -613,7 +623,7 @@ impl NaiveDate {
let days = [31, feb_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; let days = [31, feb_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let day = Ord::min(self.day(), days[(month - 1) as usize]); let day = Ord::min(self.day(), days[(month - 1) as usize]);
NaiveDate::from_mdf(year, Mdf::new(month as u32, day, flags)) NaiveDate::from_mdf(year, Mdf::new(month as u32, day, flags)?)
} }
/// Add a duration in [`Days`] to the date /// Add a duration in [`Days`] to the date
@ -974,7 +984,7 @@ impl NaiveDate {
let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)?)
} }
/// Subtracts the `days` part of given `TimeDelta` from the current date. /// Subtracts the `days` part of given `TimeDelta` from the current date.
@ -1005,7 +1015,7 @@ impl NaiveDate {
let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)) NaiveDate::from_of(year_div_400 * 400 + year_mod_400 as i32, Of::new(ordinal, flags)?)
} }
/// Subtracts another `NaiveDate` from the current date. /// Subtracts another `NaiveDate` from the current date.
@ -1042,6 +1052,19 @@ impl NaiveDate {
) )
} }
/// Returns the number of whole years from the given `base` until `self`.
pub fn years_since(&self, base: Self) -> Option<u32> {
let mut years = self.year() - base.year();
if (self.month(), self.day()) < (base.month(), base.day()) {
years -= 1;
}
match years >= 0 {
true => Some(years as u32),
false => None,
}
}
/// Formats the date with the specified formatting items. /// Formats the date with the specified formatting items.
/// Otherwise it is the same as the ordinary `format` method. /// Otherwise it is the same as the ordinary `format` method.
/// ///
@ -1427,7 +1450,7 @@ impl Datelike for NaiveDate {
/// ``` /// ```
#[inline] #[inline]
fn with_month(&self, month: u32) -> Option<NaiveDate> { fn with_month(&self, month: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_month(month)) self.with_mdf(self.mdf().with_month(month)?)
} }
/// Makes a new `NaiveDate` with the month number (starting from 0) changed. /// Makes a new `NaiveDate` with the month number (starting from 0) changed.
@ -1446,7 +1469,7 @@ impl Datelike for NaiveDate {
/// ``` /// ```
#[inline] #[inline]
fn with_month0(&self, month0: u32) -> Option<NaiveDate> { fn with_month0(&self, month0: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_month(month0 + 1)) self.with_mdf(self.mdf().with_month(month0 + 1)?)
} }
/// Makes a new `NaiveDate` with the day of month (starting from 1) changed. /// Makes a new `NaiveDate` with the day of month (starting from 1) changed.
@ -1465,7 +1488,7 @@ impl Datelike for NaiveDate {
/// ``` /// ```
#[inline] #[inline]
fn with_day(&self, day: u32) -> Option<NaiveDate> { fn with_day(&self, day: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_day(day)) self.with_mdf(self.mdf().with_day(day)?)
} }
/// Makes a new `NaiveDate` with the day of month (starting from 0) changed. /// Makes a new `NaiveDate` with the day of month (starting from 0) changed.
@ -1484,7 +1507,7 @@ impl Datelike for NaiveDate {
/// ``` /// ```
#[inline] #[inline]
fn with_day0(&self, day0: u32) -> Option<NaiveDate> { fn with_day0(&self, day0: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_day(day0 + 1)) self.with_mdf(self.mdf().with_day(day0 + 1)?)
} }
/// Makes a new `NaiveDate` with the day of year (starting from 1) changed. /// Makes a new `NaiveDate` with the day of year (starting from 1) changed.
@ -1508,7 +1531,7 @@ impl Datelike for NaiveDate {
/// ``` /// ```
#[inline] #[inline]
fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDate> { fn with_ordinal(&self, ordinal: u32) -> Option<NaiveDate> {
self.with_of(self.of().with_ordinal(ordinal)) self.with_of(self.of().with_ordinal(ordinal)?)
} }
/// Makes a new `NaiveDate` with the day of year (starting from 0) changed. /// Makes a new `NaiveDate` with the day of year (starting from 0) changed.
@ -1532,7 +1555,7 @@ impl Datelike for NaiveDate {
/// ``` /// ```
#[inline] #[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDate> { fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDate> {
self.with_of(self.of().with_ordinal(ordinal0 + 1)) self.with_of(self.of().with_ordinal(ordinal0 + 1)?)
} }
} }
@ -1818,14 +1841,22 @@ impl DoubleEndedIterator for NaiveDateWeeksIterator {
/// ``` /// ```
impl fmt::Debug for NaiveDate { impl fmt::Debug for NaiveDate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use core::fmt::Write;
let year = self.year(); let year = self.year();
let mdf = self.mdf(); let mdf = self.mdf();
if (0..=9999).contains(&year) { if (0..=9999).contains(&year) {
write!(f, "{:04}-{:02}-{:02}", year, mdf.month(), mdf.day()) write_hundreds(f, (year / 100) as u8)?;
write_hundreds(f, (year % 100) as u8)?;
} else { } else {
// ISO 8601 requires the explicit sign for out-of-range years // ISO 8601 requires the explicit sign for out-of-range years
write!(f, "{:+05}-{:02}-{:02}", year, mdf.month(), mdf.day()) write!(f, "{:+05}", year)?;
} }
f.write_char('-')?;
write_hundreds(f, mdf.month() as u8)?;
f.write_char('-')?;
write_hundreds(f, mdf.day() as u8)
} }
} }

View File

@ -6,6 +6,7 @@
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use core::borrow::Borrow; use core::borrow::Borrow;
use core::convert::TryFrom; use core::convert::TryFrom;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::{fmt, str}; use core::{fmt, str};
@ -74,6 +75,7 @@ pub const MAX_DATETIME: NaiveDateTime = NaiveDateTime::MAX;
/// ``` /// ```
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] #[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] #[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct NaiveDateTime { pub struct NaiveDateTime {
date: NaiveDate, date: NaiveDate,
time: NaiveTime, time: NaiveTime,
@ -1623,7 +1625,9 @@ impl Sub<Days> for NaiveDateTime {
/// ``` /// ```
impl fmt::Debug for NaiveDateTime { impl fmt::Debug for NaiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}T{:?}", self.date, self.time) self.date.fmt(f)?;
f.write_char('T')?;
self.time.fmt(f)
} }
} }
@ -1654,7 +1658,9 @@ impl fmt::Debug for NaiveDateTime {
/// ``` /// ```
impl fmt::Display for NaiveDateTime { impl fmt::Display for NaiveDateTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.date, self.time) self.date.fmt(f)?;
f.write_char(' ')?;
self.time.fmt(f)
} }
} }

View File

@ -1053,7 +1053,7 @@ fn test_serde_deserialize() {
// it is not self-describing. // it is not self-describing.
#[test] #[test]
fn test_serde_bincode() { fn test_serde_bincode() {
use crate::naive::NaiveDate; use crate::NaiveDate;
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap(); let dt = NaiveDate::from_ymd_opt(2016, 7, 8).unwrap().and_hms_milli_opt(9, 10, 48, 90).unwrap();
@ -1076,10 +1076,8 @@ fn test_serde_bincode_optional() {
two: Option<DateTime<Utc>>, two: Option<DateTime<Utc>>,
} }
let expected = Test { let expected =
one: Some(1), Test { one: Some(1), two: Some(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap()) };
two: Some(Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 1, 1).unwrap()),
};
let bytes: Vec<u8> = serialize(&expected).unwrap(); let bytes: Vec<u8> = serialize(&expected).unwrap();
let actual = deserialize::<Test>(&(bytes)).unwrap(); let actual = deserialize::<Test>(&(bytes)).unwrap();

View File

@ -1,6 +1,6 @@
use super::NaiveDateTime; use super::NaiveDateTime;
use crate::naive::NaiveDate;
use crate::time_delta::TimeDelta; use crate::time_delta::TimeDelta;
use crate::NaiveDate;
use crate::{Datelike, FixedOffset, Utc}; use crate::{Datelike, FixedOffset, Utc};
use std::i64; use std::i64;

View File

@ -272,20 +272,13 @@ pub(super) struct Of(pub(crate) u32);
impl Of { impl Of {
#[inline] #[inline]
fn clamp_ordinal(ordinal: u32) -> u32 { pub(super) fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Option<Of> {
if ordinal > 366 { match ordinal <= 366 {
0 true => Some(Of((ordinal << 4) | u32::from(flags))),
} else { false => None,
ordinal
} }
} }
#[inline]
pub(super) fn new(ordinal: u32, YearFlags(flags): YearFlags) -> Of {
let ordinal = Of::clamp_ordinal(ordinal);
Of((ordinal << 4) | u32::from(flags))
}
#[inline] #[inline]
pub(super) fn from_mdf(Mdf(mdf): Mdf) -> Of { pub(super) fn from_mdf(Mdf(mdf): Mdf) -> Of {
let mdl = mdf >> 3; let mdl = mdf >> 3;
@ -309,10 +302,13 @@ impl Of {
} }
#[inline] #[inline]
pub(super) fn with_ordinal(&self, ordinal: u32) -> Of { pub(super) fn with_ordinal(&self, ordinal: u32) -> Option<Of> {
let ordinal = Of::clamp_ordinal(ordinal); if ordinal > 366 {
return None;
}
let Of(of) = *self; let Of(of) = *self;
Of((of & 0b1111) | (ordinal << 4)) Some(Of((of & 0b1111) | (ordinal << 4)))
} }
#[inline] #[inline]
@ -377,30 +373,13 @@ pub(super) struct Mdf(pub(super) u32);
impl Mdf { impl Mdf {
#[inline] #[inline]
fn clamp_month(month: u32) -> u32 { pub(super) fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Option<Mdf> {
if month > 12 { match month <= 12 && day <= 31 {
0 true => Some(Mdf((month << 9) | (day << 4) | u32::from(flags))),
} else { false => None,
month
} }
} }
#[inline]
fn clamp_day(day: u32) -> u32 {
if day > 31 {
0
} else {
day
}
}
#[inline]
pub(super) fn new(month: u32, day: u32, YearFlags(flags): YearFlags) -> Mdf {
let month = Mdf::clamp_month(month);
let day = Mdf::clamp_day(day);
Mdf((month << 9) | (day << 4) | u32::from(flags))
}
#[inline] #[inline]
pub(super) fn from_of(Of(of): Of) -> Mdf { pub(super) fn from_of(Of(of): Of) -> Mdf {
let ol = of >> 3; let ol = of >> 3;
@ -427,10 +406,13 @@ impl Mdf {
} }
#[inline] #[inline]
pub(super) fn with_month(&self, month: u32) -> Mdf { pub(super) fn with_month(&self, month: u32) -> Option<Mdf> {
let month = Mdf::clamp_month(month); if month > 12 {
return None;
}
let Mdf(mdf) = *self; let Mdf(mdf) = *self;
Mdf((mdf & 0b1_1111_1111) | (month << 9)) Some(Mdf((mdf & 0b1_1111_1111) | (month << 9)))
} }
#[inline] #[inline]
@ -440,10 +422,13 @@ impl Mdf {
} }
#[inline] #[inline]
pub(super) fn with_day(&self, day: u32) -> Mdf { pub(super) fn with_day(&self, day: u32) -> Option<Mdf> {
let day = Mdf::clamp_day(day); if day > 31 {
return None;
}
let Mdf(mdf) = *self; let Mdf(mdf) = *self;
Mdf((mdf & !0b1_1111_0000) | (day << 4)) Some(Mdf((mdf & !0b1_1111_0000) | (day << 4)))
} }
#[inline] #[inline]
@ -525,7 +510,12 @@ mod tests {
fn test_of() { fn test_of() {
fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) { fn check(expected: bool, flags: YearFlags, ordinal1: u32, ordinal2: u32) {
for ordinal in range_inclusive(ordinal1, ordinal2) { for ordinal in range_inclusive(ordinal1, ordinal2) {
let of = Of::new(ordinal, flags); let of = match Of::new(ordinal, flags) {
Some(of) => of,
None if !expected => continue,
None => panic!("Of::new({}, {:?}) returned None", ordinal, flags),
};
assert!( assert!(
of.valid() == expected, of.valid() == expected,
"ordinal {} = {:?} should be {} for dominical year {:?}", "ordinal {} = {:?} should be {} for dominical year {:?}",
@ -557,7 +547,12 @@ mod tests {
fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) { fn check(expected: bool, flags: YearFlags, month1: u32, day1: u32, month2: u32, day2: u32) {
for month in range_inclusive(month1, month2) { for month in range_inclusive(month1, month2) {
for day in range_inclusive(day1, day2) { for day in range_inclusive(day1, day2) {
let mdf = Mdf::new(month, day, flags); let mdf = match Mdf::new(month, day, flags) {
Some(mdf) => mdf,
None if !expected => continue,
None => panic!("Mdf::new({}, {}, {:?}) returned None", month, day, flags),
};
assert!( assert!(
mdf.valid() == expected, mdf.valid() == expected,
"month {} day {} = {:?} should be {} for dominical year {:?}", "month {} day {} = {:?} should be {} for dominical year {:?}",
@ -642,7 +637,7 @@ mod tests {
fn test_of_fields() { fn test_of_fields() {
for &flags in FLAGS.iter() { for &flags in FLAGS.iter() {
for ordinal in range_inclusive(1u32, 366) { for ordinal in range_inclusive(1u32, 366) {
let of = Of::new(ordinal, flags); let of = Of::new(ordinal, flags).unwrap();
if of.valid() { if of.valid() {
assert_eq!(of.ordinal(), ordinal); assert_eq!(of.ordinal(), ordinal);
} }
@ -653,11 +648,16 @@ mod tests {
#[test] #[test]
fn test_of_with_fields() { fn test_of_with_fields() {
fn check(flags: YearFlags, ordinal: u32) { fn check(flags: YearFlags, ordinal: u32) {
let of = Of::new(ordinal, flags); let of = Of::new(ordinal, flags).unwrap();
for ordinal in range_inclusive(0u32, 1024) { for ordinal in range_inclusive(0u32, 1024) {
let of = of.with_ordinal(ordinal); let of = match of.with_ordinal(ordinal) {
assert_eq!(of.valid(), Of::new(ordinal, flags).valid()); Some(of) => of,
None if ordinal > 366 => continue,
None => panic!("failed to create Of with ordinal {}", ordinal),
};
assert_eq!(of.valid(), Of::new(ordinal, flags).unwrap().valid());
if of.valid() { if of.valid() {
assert_eq!(of.ordinal(), ordinal); assert_eq!(of.ordinal(), ordinal);
} }
@ -676,25 +676,25 @@ mod tests {
#[test] #[test]
fn test_of_weekday() { fn test_of_weekday() {
assert_eq!(Of::new(1, A).weekday(), Weekday::Sun); assert_eq!(Of::new(1, A).unwrap().weekday(), Weekday::Sun);
assert_eq!(Of::new(1, B).weekday(), Weekday::Sat); assert_eq!(Of::new(1, B).unwrap().weekday(), Weekday::Sat);
assert_eq!(Of::new(1, C).weekday(), Weekday::Fri); assert_eq!(Of::new(1, C).unwrap().weekday(), Weekday::Fri);
assert_eq!(Of::new(1, D).weekday(), Weekday::Thu); assert_eq!(Of::new(1, D).unwrap().weekday(), Weekday::Thu);
assert_eq!(Of::new(1, E).weekday(), Weekday::Wed); assert_eq!(Of::new(1, E).unwrap().weekday(), Weekday::Wed);
assert_eq!(Of::new(1, F).weekday(), Weekday::Tue); assert_eq!(Of::new(1, F).unwrap().weekday(), Weekday::Tue);
assert_eq!(Of::new(1, G).weekday(), Weekday::Mon); assert_eq!(Of::new(1, G).unwrap().weekday(), Weekday::Mon);
assert_eq!(Of::new(1, AG).weekday(), Weekday::Sun); assert_eq!(Of::new(1, AG).unwrap().weekday(), Weekday::Sun);
assert_eq!(Of::new(1, BA).weekday(), Weekday::Sat); assert_eq!(Of::new(1, BA).unwrap().weekday(), Weekday::Sat);
assert_eq!(Of::new(1, CB).weekday(), Weekday::Fri); assert_eq!(Of::new(1, CB).unwrap().weekday(), Weekday::Fri);
assert_eq!(Of::new(1, DC).weekday(), Weekday::Thu); assert_eq!(Of::new(1, DC).unwrap().weekday(), Weekday::Thu);
assert_eq!(Of::new(1, ED).weekday(), Weekday::Wed); assert_eq!(Of::new(1, ED).unwrap().weekday(), Weekday::Wed);
assert_eq!(Of::new(1, FE).weekday(), Weekday::Tue); assert_eq!(Of::new(1, FE).unwrap().weekday(), Weekday::Tue);
assert_eq!(Of::new(1, GF).weekday(), Weekday::Mon); assert_eq!(Of::new(1, GF).unwrap().weekday(), Weekday::Mon);
for &flags in FLAGS.iter() { for &flags in FLAGS.iter() {
let mut prev = Of::new(1, flags).weekday(); let mut prev = Of::new(1, flags).unwrap().weekday();
for ordinal in range_inclusive(2u32, flags.ndays()) { for ordinal in range_inclusive(2u32, flags.ndays()) {
let of = Of::new(ordinal, flags); let of = Of::new(ordinal, flags).unwrap();
let expected = prev.succ(); let expected = prev.succ();
assert_eq!(of.weekday(), expected); assert_eq!(of.weekday(), expected);
prev = expected; prev = expected;
@ -707,7 +707,11 @@ mod tests {
for &flags in FLAGS.iter() { for &flags in FLAGS.iter() {
for month in range_inclusive(1u32, 12) { for month in range_inclusive(1u32, 12) {
for day in range_inclusive(1u32, 31) { for day in range_inclusive(1u32, 31) {
let mdf = Mdf::new(month, day, flags); let mdf = match Mdf::new(month, day, flags) {
Some(mdf) => mdf,
None => continue,
};
if mdf.valid() { if mdf.valid() {
assert_eq!(mdf.month(), month); assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day); assert_eq!(mdf.day(), day);
@ -720,11 +724,15 @@ mod tests {
#[test] #[test]
fn test_mdf_with_fields() { fn test_mdf_with_fields() {
fn check(flags: YearFlags, month: u32, day: u32) { fn check(flags: YearFlags, month: u32, day: u32) {
let mdf = Mdf::new(month, day, flags); let mdf = Mdf::new(month, day, flags).unwrap();
for month in range_inclusive(0u32, 16) { for month in range_inclusive(0u32, 16) {
let mdf = mdf.with_month(month); let mdf = match mdf.with_month(month) {
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); Some(mdf) => mdf,
None if month > 12 => continue,
None => panic!("failed to create Mdf with month {}", month),
};
if mdf.valid() { if mdf.valid() {
assert_eq!(mdf.month(), month); assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day); assert_eq!(mdf.day(), day);
@ -732,8 +740,12 @@ mod tests {
} }
for day in range_inclusive(0u32, 1024) { for day in range_inclusive(0u32, 1024) {
let mdf = mdf.with_day(day); let mdf = match mdf.with_day(day) {
assert_eq!(mdf.valid(), Mdf::new(month, day, flags).valid()); Some(mdf) => mdf,
None if day > 31 => continue,
None => panic!("failed to create Mdf with month {}", month),
};
if mdf.valid() { if mdf.valid() {
assert_eq!(mdf.month(), month); assert_eq!(mdf.month(), month);
assert_eq!(mdf.day(), day); assert_eq!(mdf.day(), day);
@ -763,7 +775,7 @@ mod tests {
fn test_of_isoweekdate_raw() { fn test_of_isoweekdate_raw() {
for &flags in FLAGS.iter() { for &flags in FLAGS.iter() {
// January 4 should be in the first week // January 4 should be in the first week
let (week, _) = Of::new(4 /* January 4 */, flags).isoweekdate_raw(); let (week, _) = Of::new(4 /* January 4 */, flags).unwrap().isoweekdate_raw();
assert_eq!(week, 1); assert_eq!(week, 1);
} }
} }

View File

@ -14,7 +14,7 @@ use rkyv::{Archive, Deserialize, Serialize};
#[cfg(any(feature = "alloc", feature = "std", test))] #[cfg(any(feature = "alloc", feature = "std", test))]
use crate::format::DelayedFormat; use crate::format::DelayedFormat;
use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems}; use crate::format::{parse, write_hundreds, ParseError, ParseResult, Parsed, StrftimeItems};
use crate::format::{Fixed, Item, Numeric, Pad}; use crate::format::{Fixed, Item, Numeric, Pad};
use crate::{TimeDelta, Timelike}; use crate::{TimeDelta, Timelike};
@ -76,7 +76,7 @@ mod tests;
/// ///
/// let dt1 = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_micro_opt(8, 59, 59, 1_000_000).unwrap(); /// let dt1 = NaiveDate::from_ymd_opt(2015, 7, 1).unwrap().and_hms_micro_opt(8, 59, 59, 1_000_000).unwrap();
/// ///
/// let dt2 = Utc.ymd_opt(2015, 6, 30).unwrap().and_hms_nano_opt(23, 59, 59, 1_000_000_000).unwrap(); /// let dt2 = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_nano_opt(23, 59, 59, 1_000_000_000).unwrap().and_local_timezone(Utc).unwrap();
/// # let _ = (t, dt1, dt2); /// # let _ = (t, dt1, dt2);
/// ``` /// ```
/// ///
@ -157,9 +157,9 @@ mod tests;
/// will be represented as the second part being 60, as required by ISO 8601. /// will be represented as the second part being 60, as required by ISO 8601.
/// ///
/// ``` /// ```
/// use chrono::{Utc, TimeZone}; /// use chrono::{Utc, TimeZone, NaiveDate};
/// ///
/// let dt = Utc.ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 59, 59, 1_000).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z"); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:59:60Z");
/// ``` /// ```
/// ///
@ -171,12 +171,12 @@ mod tests;
/// and would be read back to the next non-leap second. /// and would be read back to the next non-leap second.
/// ///
/// ``` /// ```
/// use chrono::{DateTime, Utc, TimeZone}; /// use chrono::{DateTime, Utc, TimeZone, NaiveDate};
/// ///
/// let dt = Utc.ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 56, 4, 1_000).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2015, 6, 30).unwrap().and_hms_milli_opt(23, 56, 4, 1_000).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z");
/// ///
/// let dt = Utc.ymd_opt(2015, 6, 30).unwrap().and_hms_opt(23, 56, 5).unwrap(); /// let dt = Utc.with_ymd_and_hms(2015, 6, 30, 23, 56, 5).unwrap();
/// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z"); /// assert_eq!(format!("{:?}", dt), "2015-06-30T23:56:05Z");
/// assert_eq!(DateTime::<Utc>::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt); /// assert_eq!(DateTime::<Utc>::parse_from_rfc3339("2015-06-30T23:56:05Z").unwrap(), dt);
/// ``` /// ```
@ -190,6 +190,17 @@ pub struct NaiveTime {
frac: u32, frac: u32,
} }
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for NaiveTime {
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<NaiveTime> {
let secs = u.int_in_range(0..=86_399)?;
let nano = u.int_in_range(0..=1_999_999_999)?;
let time = NaiveTime::from_num_seconds_from_midnight_opt(secs, nano)
.expect("Could not generate a valid chrono::NaiveTime. It looks like implementation of Arbitrary for NaiveTime is erroneous.");
Ok(time)
}
}
impl NaiveTime { impl NaiveTime {
/// Makes a new `NaiveTime` from hour, minute and second. /// Makes a new `NaiveTime` from hour, minute and second.
/// ///
@ -637,13 +648,7 @@ impl NaiveTime {
// `secs` may contain a leap second yet to be counted // `secs` may contain a leap second yet to be counted
let adjust = match self.secs.cmp(&rhs.secs) { let adjust = match self.secs.cmp(&rhs.secs) {
Ordering::Greater => { Ordering::Greater => i64::from(rhs.frac >= 1_000_000_000),
if rhs.frac >= 1_000_000_000 {
1
} else {
0
}
}
Ordering::Equal => 0, Ordering::Equal => 0,
Ordering::Less => { Ordering::Less => {
if self.frac >= 1_000_000_000 { if self.frac >= 1_000_000_000 {
@ -1173,7 +1178,13 @@ impl fmt::Debug for NaiveTime {
(sec, self.frac) (sec, self.frac)
}; };
write!(f, "{:02}:{:02}:{:02}", hour, min, sec)?; use core::fmt::Write;
write_hundreds(f, hour as u8)?;
f.write_char(':')?;
write_hundreds(f, min as u8)?;
f.write_char(':')?;
write_hundreds(f, sec as u8)?;
if nano == 0 { if nano == 0 {
Ok(()) Ok(())
} else if nano % 1_000_000 == 0 { } else if nano % 1_000_000 == 0 {

View File

@ -152,6 +152,16 @@ impl fmt::Display for FixedOffset {
} }
} }
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for FixedOffset {
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<FixedOffset> {
let secs = u.int_in_range(-86_399..=86_399)?;
let fixed_offset = FixedOffset::east_opt(secs)
.expect("Could not generate a valid chrono::FixedOffset. It looks like implementation of Arbitrary for FixedOffset is erroneous.");
Ok(fixed_offset)
}
}
// addition or subtraction of FixedOffset to/from Timelike values is the same as // addition or subtraction of FixedOffset to/from Timelike values is the same as
// adding or subtracting the offset's local_minus_utc value // adding or subtracting the offset's local_minus_utc value
// but keep keeps the leap second information. // but keep keeps the leap second information.
@ -231,33 +241,41 @@ mod tests {
// starting from 0.3 we don't have an offset exceeding one day. // starting from 0.3 we don't have an offset exceeding one day.
// this makes everything easier! // this makes everything easier!
assert_eq!( assert_eq!(
format!("{:?}", FixedOffset::east_opt(86399).unwrap().ymd_opt(2012, 2, 29).unwrap()), format!(
"2012-02-29+23:59:59".to_string() "{:?}",
FixedOffset::east_opt(86399)
.unwrap()
.with_ymd_and_hms(2012, 2, 29, 5, 6, 7)
.unwrap()
),
"2012-02-29T05:06:07+23:59:59".to_string()
); );
assert_eq!( assert_eq!(
format!( format!(
"{:?}", "{:?}",
FixedOffset::east_opt(86399) FixedOffset::east_opt(86399)
.unwrap() .unwrap()
.ymd_opt(2012, 2, 29) .with_ymd_and_hms(2012, 2, 29, 5, 6, 7)
.unwrap()
.and_hms_opt(5, 6, 7)
.unwrap() .unwrap()
), ),
"2012-02-29T05:06:07+23:59:59".to_string() "2012-02-29T05:06:07+23:59:59".to_string()
); );
assert_eq!(
format!("{:?}", FixedOffset::west_opt(86399).unwrap().ymd_opt(2012, 3, 4).unwrap()),
"2012-03-04-23:59:59".to_string()
);
assert_eq!( assert_eq!(
format!( format!(
"{:?}", "{:?}",
FixedOffset::west_opt(86399) FixedOffset::west_opt(86399)
.unwrap() .unwrap()
.ymd_opt(2012, 3, 4) .with_ymd_and_hms(2012, 3, 4, 5, 6, 7)
.unwrap() .unwrap()
.and_hms_opt(5, 6, 7) ),
"2012-03-04T05:06:07-23:59:59".to_string()
);
assert_eq!(
format!(
"{:?}",
FixedOffset::west_opt(86399)
.unwrap()
.with_ymd_and_hms(2012, 3, 4, 5, 6, 7)
.unwrap() .unwrap()
), ),
"2012-03-04T05:06:07-23:59:59".to_string() "2012-03-04T05:06:07-23:59:59".to_string()

View File

@ -9,6 +9,7 @@ use rkyv::{Archive, Deserialize, Serialize};
use super::fixed::FixedOffset; use super::fixed::FixedOffset;
use super::{LocalResult, TimeZone}; use super::{LocalResult, TimeZone};
use crate::naive::{NaiveDate, NaiveDateTime}; use crate::naive::{NaiveDate, NaiveDateTime};
#[allow(deprecated)]
use crate::{Date, DateTime}; use crate::{Date, DateTime};
// we don't want `stub.rs` when the target_os is not wasi or emscripten // we don't want `stub.rs` when the target_os is not wasi or emscripten
@ -52,10 +53,13 @@ mod tz_info;
/// ``` /// ```
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] #[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Local; pub struct Local;
impl Local { impl Local {
/// Returns a `Date` which corresponds to the current date. /// Returns a `Date` which corresponds to the current date.
#[deprecated(since = "0.4.23", note = "use `Local::now()` instead")]
#[allow(deprecated)]
pub fn today() -> Date<Local> { pub fn today() -> Date<Local> {
Local::now().date() Local::now().date()
} }
@ -96,6 +100,7 @@ impl TimeZone for Local {
} }
// they are easier to define in terms of the finished date and time unlike other offsets // they are easier to define in terms of the finished date and time unlike other offsets
#[allow(deprecated)]
fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> { fn offset_from_local_date(&self, local: &NaiveDate) -> LocalResult<FixedOffset> {
self.from_local_date(local).map(|date| *date.offset()) self.from_local_date(local).map(|date| *date.offset())
} }
@ -104,6 +109,7 @@ impl TimeZone for Local {
self.from_local_datetime(local).map(|datetime| *datetime.offset()) self.from_local_datetime(local).map(|datetime| *datetime.offset())
} }
#[allow(deprecated)]
fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset { fn offset_from_utc_date(&self, utc: &NaiveDate) -> FixedOffset {
*self.from_utc_date(utc).offset() *self.from_utc_date(utc).offset()
} }
@ -113,6 +119,7 @@ impl TimeZone for Local {
} }
// override them for avoiding redundant works // override them for avoiding redundant works
#[allow(deprecated)]
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> { fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Local>> {
// this sounds very strange, but required for keeping `TimeZone::ymd` sane. // this sounds very strange, but required for keeping `TimeZone::ymd` sane.
// in the other words, we use the offset at the local midnight // in the other words, we use the offset at the local midnight
@ -145,6 +152,7 @@ impl TimeZone for Local {
inner::naive_to_local(local, true) inner::naive_to_local(local, true)
} }
#[allow(deprecated)]
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> { fn from_utc_date(&self, utc: &NaiveDate) -> Date<Local> {
let midnight = self.from_utc_datetime(&utc.and_hms_opt(0, 0, 0).unwrap()); let midnight = self.from_utc_datetime(&utc.and_hms_opt(0, 0, 0).unwrap());
Date::from_utc(*utc, *midnight.offset()) Date::from_utc(*utc, *midnight.offset())
@ -179,7 +187,7 @@ impl TimeZone for Local {
mod tests { mod tests {
use super::Local; use super::Local;
use crate::offset::TimeZone; use crate::offset::TimeZone;
use crate::{Datelike, TimeDelta}; use crate::{Datelike, TimeDelta, Utc};
#[test] #[test]
fn verify_correct_offsets() { fn verify_correct_offsets() {
@ -227,13 +235,13 @@ mod tests {
#[test] #[test]
fn test_local_date_sanity_check() { fn test_local_date_sanity_check() {
// issue #27 // issue #27
assert_eq!(Local.ymd_opt(2999, 12, 28).unwrap().day(), 28); assert_eq!(Local.with_ymd_and_hms(2999, 12, 28, 0, 0, 0).unwrap().day(), 28);
} }
#[test] #[test]
fn test_leap_second() { fn test_leap_second() {
// issue #123 // issue #123
let today = Local::today(); let today = Utc::now().date_naive();
let dt = today.and_hms_milli_opt(1, 2, 59, 1000).unwrap(); let dt = today.and_hms_milli_opt(1, 2, 59, 1000).unwrap();
let timestr = dt.time().to_string(); let timestr = dt.time().to_string();

View File

@ -26,11 +26,10 @@ impl TimeZone {
/// ///
/// This method in not supported on non-UNIX platforms, and returns the UTC time zone instead. /// This method in not supported on non-UNIX platforms, and returns the UTC time zone instead.
/// ///
pub(crate) fn local() -> Result<Self, Error> { pub(crate) fn local(env_tz: Option<&str>) -> Result<Self, Error> {
if let Ok(tz) = std::env::var("TZ") { match env_tz {
Self::from_posix_tz(&tz) Some(tz) => Self::from_posix_tz(tz),
} else { None => Self::from_posix_tz("localtime"),
Self::from_posix_tz("localtime")
} }
} }
@ -813,7 +812,7 @@ mod tests {
// so just ensure that ::local() acts as expected // so just ensure that ::local() acts as expected
// in this case // in this case
if let Ok(tz) = std::env::var("TZ") { if let Ok(tz) = std::env::var("TZ") {
let time_zone_local = TimeZone::local()?; let time_zone_local = TimeZone::local(Some(tz.as_str()))?;
let time_zone_local_1 = TimeZone::from_posix_tz(&tz)?; let time_zone_local_1 = TimeZone::from_posix_tz(&tz)?;
assert_eq!(time_zone_local, time_zone_local_1); assert_eq!(time_zone_local, time_zone_local_1);
} }

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use std::{cell::RefCell, env, fs, time::SystemTime}; use std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime};
use super::tz_info::TimeZone; use super::tz_info::TimeZone;
use super::{DateTime, FixedOffset, Local, NaiveDateTime}; use super::{DateTime, FixedOffset, Local, NaiveDateTime};
@ -32,68 +32,40 @@ thread_local! {
} }
enum Source { enum Source {
LocalTime { mtime: SystemTime, last_checked: SystemTime }, LocalTime { mtime: SystemTime },
// we don't bother storing the contents of the environment variable in this case. Environment { hash: u64 },
// changing the environment while the process is running is generally not reccomended
Environment,
} }
impl Default for Source { impl Source {
fn default() -> Source { fn new(env_tz: Option<&str>) -> Source {
// use of var_os avoids allocating, which is nice match env_tz {
// as we are only going to discard the string anyway Some(tz) => {
// but we must ensure the contents are valid unicode let mut hasher = hash_map::DefaultHasher::new();
// otherwise the behaivour here would be different hasher.write(tz.as_bytes());
// to that in `naive_to_local` let hash = hasher.finish();
match env::var_os("TZ") { Source::Environment { hash }
Some(ref s) if s.to_str().is_some() => Source::Environment, }
Some(_) | None => match fs::symlink_metadata("/etc/localtime") { None => match fs::symlink_metadata("/etc/localtime") {
Ok(data) => Source::LocalTime { Ok(data) => Source::LocalTime {
// we have to pick a sensible default when the mtime fails // we have to pick a sensible default when the mtime fails
// by picking SystemTime::now() we raise the probability of // by picking SystemTime::now() we raise the probability of
// the cache being invalidated if/when the mtime starts working // the cache being invalidated if/when the mtime starts working
mtime: data.modified().unwrap_or_else(|_| SystemTime::now()), mtime: data.modified().unwrap_or_else(|_| SystemTime::now()),
last_checked: SystemTime::now(),
}, },
Err(_) => { Err(_) => {
// as above, now() should be a better default than some constant // as above, now() should be a better default than some constant
// TODO: see if we can improve caching in the case where the fallback is a valid timezone // TODO: see if we can improve caching in the case where the fallback is a valid timezone
Source::LocalTime { mtime: SystemTime::now(), last_checked: SystemTime::now() } Source::LocalTime { mtime: SystemTime::now() }
} }
}, },
} }
} }
} }
impl Source {
fn out_of_date(&mut self) -> bool {
let now = SystemTime::now();
let prev = match self {
Source::LocalTime { mtime, last_checked } => match now.duration_since(*last_checked) {
Ok(d) if d.as_secs() < 1 => return false,
Ok(_) | Err(_) => *mtime,
},
Source::Environment => return false,
};
match Source::default() {
Source::LocalTime { mtime, .. } => {
*self = Source::LocalTime { mtime, last_checked: now };
prev != mtime
}
// will only reach here if TZ has been set while
// the process is running
Source::Environment => {
*self = Source::Environment;
true
}
}
}
}
struct Cache { struct Cache {
zone: TimeZone, zone: TimeZone,
source: Source, source: Source,
last_checked: SystemTime,
} }
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
@ -115,17 +87,63 @@ fn fallback_timezone() -> Option<TimeZone> {
impl Default for Cache { impl Default for Cache {
fn default() -> Cache { fn default() -> Cache {
// default to UTC if no local timezone can be found // default to UTC if no local timezone can be found
let env_tz = env::var("TZ").ok();
let env_ref = env_tz.as_ref().map(|s| s.as_str());
Cache { Cache {
zone: TimeZone::local().ok().or_else(fallback_timezone).unwrap_or_else(TimeZone::utc), last_checked: SystemTime::now(),
source: Source::default(), source: Source::new(env_ref),
zone: current_zone(env_ref),
} }
} }
} }
fn current_zone(var: Option<&str>) -> TimeZone {
TimeZone::local(var).ok().or_else(fallback_timezone).unwrap_or_else(TimeZone::utc)
}
impl Cache { impl Cache {
fn offset(&mut self, d: NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> { fn offset(&mut self, d: NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
if self.source.out_of_date() { let now = SystemTime::now();
*self = Cache::default();
match now.duration_since(self.last_checked) {
// If the cache has been around for less than a second then we reuse it
// unconditionally. This is a reasonable tradeoff because the timezone
// generally won't be changing _that_ often, but if the time zone does
// change, it will reflect sufficiently quickly from an application
// user's perspective.
Ok(d) if d.as_secs() < 1 => (),
Ok(_) | Err(_) => {
let env_tz = env::var("TZ").ok();
let env_ref = env_tz.as_ref().map(|s| s.as_str());
let new_source = Source::new(env_ref);
let out_of_date = match (&self.source, &new_source) {
// change from env to file or file to env, must recreate the zone
(Source::Environment { .. }, Source::LocalTime { .. })
| (Source::LocalTime { .. }, Source::Environment { .. }) => true,
// stay as file, but mtime has changed
(Source::LocalTime { mtime: old_mtime }, Source::LocalTime { mtime })
if old_mtime != mtime =>
{
true
}
// stay as env, but hash of variable has changed
(Source::Environment { hash: old_hash }, Source::Environment { hash })
if old_hash != hash =>
{
true
}
// cache can be reused
_ => false,
};
if out_of_date {
self.zone = current_zone(env_ref);
}
self.last_checked = now;
self.source = new_source;
}
} }
if !local { if !local {

View File

@ -23,6 +23,7 @@ use core::fmt;
use crate::format::{parse, ParseResult, Parsed, StrftimeItems}; use crate::format::{parse, ParseResult, Parsed, StrftimeItems};
use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime}; use crate::naive::{NaiveDate, NaiveDateTime, NaiveTime};
use crate::Weekday; use crate::Weekday;
#[allow(deprecated)]
use crate::{Date, DateTime}; use crate::{Date, DateTime};
mod fixed; mod fixed;
@ -84,6 +85,7 @@ impl<T> LocalResult<T> {
} }
} }
#[allow(deprecated)]
impl<Tz: TimeZone> LocalResult<Date<Tz>> { impl<Tz: TimeZone> LocalResult<Date<Tz>> {
/// Makes a new `DateTime` from the current date and given `NaiveTime`. /// Makes a new `DateTime` from the current date and given `NaiveTime`.
/// The offset in the current date is preserved. /// The offset in the current date is preserved.
@ -206,6 +208,27 @@ pub trait TimeZone: Sized + Clone {
/// The original `TimeZone` value can be recovered via `TimeZone::from_offset`. /// The original `TimeZone` value can be recovered via `TimeZone::from_offset`.
type Offset: Offset; type Offset: Offset;
/// Make a new `DateTime` from year, month, day, time components and current time zone.
///
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
///
/// Returns `LocalResult::None` on invalid input data.
fn with_ymd_and_hms(
&self,
year: i32,
month: u32,
day: u32,
hour: u32,
min: u32,
sec: u32,
) -> LocalResult<DateTime<Self>> {
match NaiveDate::from_ymd_opt(year, month, day).and_then(|d| d.and_hms_opt(hour, min, sec))
{
Some(dt) => self.from_local_datetime(&dt),
None => LocalResult::None,
}
}
/// Makes a new `Date` from year, month, day and the current time zone. /// Makes a new `Date` from year, month, day and the current time zone.
/// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE. /// This assumes the proleptic Gregorian calendar, with the year 0 being 1 BCE.
/// ///
@ -213,7 +236,8 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date. /// but it will propagate to the `DateTime` values constructed via this date.
/// ///
/// Panics on the out-of-range date, invalid month and/or day. /// Panics on the out-of-range date, invalid month and/or day.
#[deprecated(since = "0.4.23", note = "use `ymd_opt()` instead")] #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
#[allow(deprecated)]
fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> { fn ymd(&self, year: i32, month: u32, day: u32) -> Date<Self> {
self.ymd_opt(year, month, day).unwrap() self.ymd_opt(year, month, day).unwrap()
} }
@ -225,15 +249,8 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date. /// but it will propagate to the `DateTime` values constructed via this date.
/// ///
/// Returns `None` on the out-of-range date, invalid month and/or day. /// Returns `None` on the out-of-range date, invalid month and/or day.
/// #[deprecated(since = "0.4.23", note = "use `with_ymd_and_hms()` instead")]
/// # Example #[allow(deprecated)]
///
/// ```
/// use chrono::{Utc, LocalResult, TimeZone};
///
/// assert_eq!(Utc.ymd_opt(2015, 5, 15).unwrap().to_string(), "2015-05-15UTC");
/// assert_eq!(Utc.ymd_opt(2000, 0, 0), LocalResult::None);
/// ```
fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> { fn ymd_opt(&self, year: i32, month: u32, day: u32) -> LocalResult<Date<Self>> {
match NaiveDate::from_ymd_opt(year, month, day) { match NaiveDate::from_ymd_opt(year, month, day) {
Some(d) => self.from_local_date(&d), Some(d) => self.from_local_date(&d),
@ -248,7 +265,11 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date. /// but it will propagate to the `DateTime` values constructed via this date.
/// ///
/// Panics on the out-of-range date and/or invalid DOY. /// Panics on the out-of-range date and/or invalid DOY.
#[deprecated(since = "0.4.23", note = "use `ymd_opt()` instead")] #[deprecated(
since = "0.4.23",
note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
)]
#[allow(deprecated)]
fn yo(&self, year: i32, ordinal: u32) -> Date<Self> { fn yo(&self, year: i32, ordinal: u32) -> Date<Self> {
self.yo_opt(year, ordinal).unwrap() self.yo_opt(year, ordinal).unwrap()
} }
@ -260,14 +281,11 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date. /// but it will propagate to the `DateTime` values constructed via this date.
/// ///
/// Returns `None` on the out-of-range date and/or invalid DOY. /// Returns `None` on the out-of-range date and/or invalid DOY.
/// #[deprecated(
/// # Example since = "0.4.23",
/// note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
/// ``` )]
/// use chrono::{Utc, TimeZone}; #[allow(deprecated)]
///
/// assert_eq!(Utc.yo_opt(2015, 135).unwrap().to_string(), "2015-05-15UTC");
/// ```
fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> { fn yo_opt(&self, year: i32, ordinal: u32) -> LocalResult<Date<Self>> {
match NaiveDate::from_yo_opt(year, ordinal) { match NaiveDate::from_yo_opt(year, ordinal) {
Some(d) => self.from_local_date(&d), Some(d) => self.from_local_date(&d),
@ -284,7 +302,11 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date. /// but it will propagate to the `DateTime` values constructed via this date.
/// ///
/// Panics on the out-of-range date and/or invalid week number. /// Panics on the out-of-range date and/or invalid week number.
#[deprecated(since = "0.4.23", note = "use `isoywd_opt()` instead")] #[deprecated(
since = "0.4.23",
note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
)]
#[allow(deprecated)]
fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> { fn isoywd(&self, year: i32, week: u32, weekday: Weekday) -> Date<Self> {
self.isoywd_opt(year, week, weekday).unwrap() self.isoywd_opt(year, week, weekday).unwrap()
} }
@ -298,14 +320,11 @@ pub trait TimeZone: Sized + Clone {
/// but it will propagate to the `DateTime` values constructed via this date. /// but it will propagate to the `DateTime` values constructed via this date.
/// ///
/// Returns `None` on the out-of-range date and/or invalid week number. /// Returns `None` on the out-of-range date and/or invalid week number.
/// #[deprecated(
/// # Example since = "0.4.23",
/// note = "use `from_local_datetime()` with a `NaiveDateTime` instead"
/// ``` )]
/// use chrono::{Utc, Weekday, TimeZone}; #[allow(deprecated)]
///
/// assert_eq!(Utc.isoywd_opt(2015, 20, Weekday::Fri).unwrap().to_string(), "2015-05-15UTC");
/// ```
fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> { fn isoywd_opt(&self, year: i32, week: u32, weekday: Weekday) -> LocalResult<Date<Self>> {
match NaiveDate::from_isoywd_opt(year, week, weekday) { match NaiveDate::from_isoywd_opt(year, week, weekday) {
Some(d) => self.from_local_date(&d), Some(d) => self.from_local_date(&d),
@ -431,6 +450,8 @@ pub trait TimeZone: Sized + Clone {
/// Converts the local `NaiveDate` to the timezone-aware `Date` if possible. /// Converts the local `NaiveDate` to the timezone-aware `Date` if possible.
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[deprecated(since = "0.4.23", note = "use `from_local_datetime()` instead")]
#[allow(deprecated)]
fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> { fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<Self>> {
self.offset_from_local_date(local).map(|offset| { self.offset_from_local_date(local).map(|offset| {
// since FixedOffset is within +/- 1 day, the date is never affected // since FixedOffset is within +/- 1 day, the date is never affected
@ -454,6 +475,8 @@ pub trait TimeZone: Sized + Clone {
/// Converts the UTC `NaiveDate` to the local time. /// Converts the UTC `NaiveDate` to the local time.
/// The UTC is continuous and thus this cannot fail (but can give the duplicate local time). /// The UTC is continuous and thus this cannot fail (but can give the duplicate local time).
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[deprecated(since = "0.4.23", note = "use `from_utc_datetime()` instead")]
#[allow(deprecated)]
fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> { fn from_utc_date(&self, utc: &NaiveDate) -> Date<Self> {
Date::from_utc(*utc, self.offset_from_utc_date(utc)) Date::from_utc(*utc, self.offset_from_utc_date(utc))
} }

View File

@ -20,6 +20,7 @@ use rkyv::{Archive, Deserialize, Serialize};
use super::{FixedOffset, LocalResult, Offset, TimeZone}; use super::{FixedOffset, LocalResult, Offset, TimeZone};
use crate::naive::{NaiveDate, NaiveDateTime}; use crate::naive::{NaiveDate, NaiveDateTime};
#[cfg(feature = "clock")] #[cfg(feature = "clock")]
#[allow(deprecated)]
use crate::{Date, DateTime}; use crate::{Date, DateTime};
/// The UTC time zone. This is the most efficient time zone when you don't need the local time. /// The UTC time zone. This is the most efficient time zone when you don't need the local time.
@ -37,16 +38,22 @@ use crate::{Date, DateTime};
/// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); /// let dt = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc);
/// ///
/// assert_eq!(Utc.timestamp(61, 0), dt); /// assert_eq!(Utc.timestamp(61, 0), dt);
/// assert_eq!(Utc.ymd_opt(1970, 1, 1).unwrap().and_hms_opt(0, 1, 1).unwrap(), dt); /// assert_eq!(Utc.with_ymd_and_hms(1970, 1, 1, 0, 1, 1).unwrap(), dt);
/// ``` /// ```
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] #[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Utc; pub struct Utc;
#[cfg(feature = "clock")] #[cfg(feature = "clock")]
#[cfg_attr(docsrs, doc(cfg(feature = "clock")))] #[cfg_attr(docsrs, doc(cfg(feature = "clock")))]
impl Utc { impl Utc {
/// Returns a `Date` which corresponds to the current date. /// Returns a `Date` which corresponds to the current date.
#[deprecated(
since = "0.4.23",
note = "use `Utc::now()` instead, potentially with `.date_naive()`"
)]
#[allow(deprecated)]
pub fn today() -> Date<Utc> { pub fn today() -> Date<Utc> {
Utc::now().date() Utc::now().date()
} }

View File

@ -24,8 +24,8 @@ pub trait SubsecRound {
/// ///
/// # Example /// # Example
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc}; /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc, NaiveDate};
/// let dt = Utc.ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000); /// assert_eq!(dt.round_subsecs(2).nanosecond(), 150_000_000);
/// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000); /// assert_eq!(dt.round_subsecs(1).nanosecond(), 200_000_000);
/// ``` /// ```
@ -36,8 +36,8 @@ pub trait SubsecRound {
/// ///
/// # Example /// # Example
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc}; /// # use chrono::{DateTime, SubsecRound, Timelike, TimeZone, Utc, NaiveDate};
/// let dt = Utc.ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000); /// assert_eq!(dt.trunc_subsecs(2).nanosecond(), 150_000_000);
/// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000); /// assert_eq!(dt.trunc_subsecs(1).nanosecond(), 100_000_000);
/// ``` /// ```
@ -111,8 +111,8 @@ pub trait DurationRound: Sized {
/// ///
/// # Example /// # Example
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, DurationRound, TimeDelta, TimeZone, Utc}; /// # use chrono::{DateTime, DurationRound, TimeDelta, TimeZone, Utc, NaiveDate};
/// let dt = Utc.ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!( /// assert_eq!(
/// dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(), /// dt.duration_round(TimeDelta::milliseconds(10)).unwrap().to_string(),
/// "2018-01-11 12:00:00.150 UTC" /// "2018-01-11 12:00:00.150 UTC"
@ -128,8 +128,8 @@ pub trait DurationRound: Sized {
/// ///
/// # Example /// # Example
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, DurationRound, TimeDelta, TimeZone, Utc}; /// # use chrono::{DateTime, DurationRound, TimeDelta, TimeZone, Utc, NaiveDate};
/// let dt = Utc.ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2018, 1, 11).unwrap().and_hms_milli_opt(12, 0, 0, 154).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!( /// assert_eq!(
/// dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(), /// dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
/// "2018-01-11 12:00:00.150 UTC" /// "2018-01-11 12:00:00.150 UTC"
@ -244,7 +244,7 @@ pub enum RoundingError {
/// ///
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, DurationRound, TimeDelta, RoundingError, TimeZone, Utc}; /// # use chrono::{DateTime, DurationRound, TimeDelta, RoundingError, TimeZone, Utc};
/// let dt = Utc.ymd_opt(1970, 12, 12).unwrap().and_hms_opt(0, 0, 0).unwrap(); /// let dt = Utc.with_ymd_and_hms(1970, 12, 12, 0, 0, 0).unwrap();
/// ///
/// assert_eq!( /// assert_eq!(
/// dt.duration_round(TimeDelta::days(365)), /// dt.duration_round(TimeDelta::days(365)),
@ -256,8 +256,8 @@ pub enum RoundingError {
/// Error when `TimeDelta.num_nanoseconds` exceeds the limit. /// Error when `TimeDelta.num_nanoseconds` exceeds the limit.
/// ///
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, DurationRound, TimeDelta, RoundingError, TimeZone, Utc}; /// # use chrono::{DateTime, DurationRound, TimeDelta, RoundingError, TimeZone, Utc, NaiveDate};
/// let dt = Utc.ymd_opt(2260, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 1_75_500_000).unwrap(); /// let dt = NaiveDate::from_ymd_opt(2260, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 1_75_500_000).unwrap().and_local_timezone(Utc).unwrap();
/// ///
/// assert_eq!( /// assert_eq!(
/// dt.duration_round(TimeDelta::days(300 * 365)), /// dt.duration_round(TimeDelta::days(300 * 365)),
@ -270,7 +270,7 @@ pub enum RoundingError {
/// ///
/// ``` rust /// ``` rust
/// # use chrono::{DateTime, DurationRound, TimeDelta, RoundingError, TimeZone, Utc}; /// # use chrono::{DateTime, DurationRound, TimeDelta, RoundingError, TimeZone, Utc};
/// let dt = Utc.ymd_opt(2300, 12, 12).unwrap().and_hms_opt(0, 0, 0).unwrap(); /// let dt = Utc.with_ymd_and_hms(2300, 12, 12, 0, 0, 0).unwrap();
/// ///
/// assert_eq!(dt.duration_round(TimeDelta::days(1)), Err(RoundingError::TimestampExceedsLimit),); /// assert_eq!(dt.duration_round(TimeDelta::days(1)), Err(RoundingError::TimestampExceedsLimit),);
/// ``` /// ```
@ -306,12 +306,20 @@ impl std::error::Error for RoundingError {
mod tests { mod tests {
use super::{DurationRound, SubsecRound, TimeDelta}; use super::{DurationRound, SubsecRound, TimeDelta};
use crate::offset::{FixedOffset, TimeZone, Utc}; use crate::offset::{FixedOffset, TimeZone, Utc};
use crate::NaiveDate;
use crate::Timelike; use crate::Timelike;
#[test] #[test]
fn test_round_subsecs() { fn test_round_subsecs() {
let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
let dt = pst.ymd_opt(2018, 1, 11).unwrap().and_hms_nano_opt(10, 5, 13, 84_660_684).unwrap(); let dt = pst
.from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 13, 84_660_684)
.unwrap(),
)
.unwrap();
assert_eq!(dt.round_subsecs(10), dt); assert_eq!(dt.round_subsecs(10), dt);
assert_eq!(dt.round_subsecs(9), dt); assert_eq!(dt.round_subsecs(9), dt);
@ -327,8 +335,14 @@ mod tests {
assert_eq!(dt.round_subsecs(0).nanosecond(), 0); assert_eq!(dt.round_subsecs(0).nanosecond(), 0);
assert_eq!(dt.round_subsecs(0).second(), 13); assert_eq!(dt.round_subsecs(0).second(), 13);
let dt = let dt = Utc
Utc.ymd_opt(2018, 1, 11).unwrap().and_hms_nano_opt(10, 5, 27, 750_500_000).unwrap(); .from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 27, 750_500_000)
.unwrap(),
)
.unwrap();
assert_eq!(dt.round_subsecs(9), dt); assert_eq!(dt.round_subsecs(9), dt);
assert_eq!(dt.round_subsecs(4), dt); assert_eq!(dt.round_subsecs(4), dt);
assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000); assert_eq!(dt.round_subsecs(3).nanosecond(), 751_000_000);
@ -341,8 +355,14 @@ mod tests {
#[test] #[test]
fn test_round_leap_nanos() { fn test_round_leap_nanos() {
let dt = let dt = Utc
Utc.ymd_opt(2016, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 1_750_500_000).unwrap(); .from_local_datetime(
&NaiveDate::from_ymd_opt(2016, 12, 31)
.unwrap()
.and_hms_nano_opt(23, 59, 59, 1_750_500_000)
.unwrap(),
)
.unwrap();
assert_eq!(dt.round_subsecs(9), dt); assert_eq!(dt.round_subsecs(9), dt);
assert_eq!(dt.round_subsecs(4), dt); assert_eq!(dt.round_subsecs(4), dt);
assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000); assert_eq!(dt.round_subsecs(2).nanosecond(), 1_750_000_000);
@ -356,7 +376,14 @@ mod tests {
#[test] #[test]
fn test_trunc_subsecs() { fn test_trunc_subsecs() {
let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap(); let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
let dt = pst.ymd_opt(2018, 1, 11).unwrap().and_hms_nano_opt(10, 5, 13, 84_660_684).unwrap(); let dt = pst
.from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 13, 84_660_684)
.unwrap(),
)
.unwrap();
assert_eq!(dt.trunc_subsecs(10), dt); assert_eq!(dt.trunc_subsecs(10), dt);
assert_eq!(dt.trunc_subsecs(9), dt); assert_eq!(dt.trunc_subsecs(9), dt);
@ -372,8 +399,14 @@ mod tests {
assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0); assert_eq!(dt.trunc_subsecs(0).nanosecond(), 0);
assert_eq!(dt.trunc_subsecs(0).second(), 13); assert_eq!(dt.trunc_subsecs(0).second(), 13);
let dt = let dt = pst
pst.ymd_opt(2018, 1, 11).unwrap().and_hms_nano_opt(10, 5, 27, 750_500_000).unwrap(); .from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 27, 750_500_000)
.unwrap(),
)
.unwrap();
assert_eq!(dt.trunc_subsecs(9), dt); assert_eq!(dt.trunc_subsecs(9), dt);
assert_eq!(dt.trunc_subsecs(4), dt); assert_eq!(dt.trunc_subsecs(4), dt);
assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000); assert_eq!(dt.trunc_subsecs(3).nanosecond(), 750_000_000);
@ -386,8 +419,14 @@ mod tests {
#[test] #[test]
fn test_trunc_leap_nanos() { fn test_trunc_leap_nanos() {
let dt = let dt = Utc
Utc.ymd_opt(2016, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 1_750_500_000).unwrap(); .from_local_datetime(
&NaiveDate::from_ymd_opt(2016, 12, 31)
.unwrap()
.and_hms_nano_opt(23, 59, 59, 1_750_500_000)
.unwrap(),
)
.unwrap();
assert_eq!(dt.trunc_subsecs(9), dt); assert_eq!(dt.trunc_subsecs(9), dt);
assert_eq!(dt.trunc_subsecs(4), dt); assert_eq!(dt.trunc_subsecs(4), dt);
assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000); assert_eq!(dt.trunc_subsecs(2).nanosecond(), 1_750_000_000);
@ -400,8 +439,14 @@ mod tests {
#[test] #[test]
fn test_duration_round() { fn test_duration_round() {
let dt = let dt = Utc
Utc.ymd_opt(2016, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 175_500_000).unwrap(); .from_local_datetime(
&NaiveDate::from_ymd_opt(2016, 12, 31)
.unwrap()
.and_hms_nano_opt(23, 59, 59, 175_500_000)
.unwrap(),
)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_round(TimeDelta::zero()).unwrap().to_string(), dt.duration_round(TimeDelta::zero()).unwrap().to_string(),
@ -414,13 +459,27 @@ mod tests {
); );
// round up // round up
let dt = Utc.ymd_opt(2012, 12, 12).unwrap().and_hms_milli_opt(18, 22, 30, 0).unwrap(); let dt = Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2012, 12, 12)
.unwrap()
.and_hms_milli_opt(18, 22, 30, 0)
.unwrap(),
)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(), dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:25:00 UTC" "2012-12-12 18:25:00 UTC"
); );
// round down // round down
let dt = Utc.ymd_opt(2012, 12, 12).unwrap().and_hms_milli_opt(18, 22, 29, 999).unwrap(); let dt = Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2012, 12, 12)
.unwrap()
.and_hms_milli_opt(18, 22, 29, 999)
.unwrap(),
)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(), dt.duration_round(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC" "2012-12-12 18:20:00 UTC"
@ -444,12 +503,8 @@ mod tests {
); );
// timezone east // timezone east
let dt = FixedOffset::east_opt(3600) let dt =
.unwrap() FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
.ymd_opt(2020, 10, 27)
.unwrap()
.and_hms_opt(15, 0, 0)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_round(TimeDelta::days(1)).unwrap().to_string(), dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-28 00:00:00 +01:00" "2020-10-28 00:00:00 +01:00"
@ -460,12 +515,8 @@ mod tests {
); );
// timezone west // timezone west
let dt = FixedOffset::west_opt(3600) let dt =
.unwrap() FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
.ymd_opt(2020, 10, 27)
.unwrap()
.and_hms_opt(15, 0, 0)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_round(TimeDelta::days(1)).unwrap().to_string(), dt.duration_round(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-28 00:00:00 -01:00" "2020-10-28 00:00:00 -01:00"
@ -479,9 +530,12 @@ mod tests {
#[test] #[test]
fn test_duration_round_naive() { fn test_duration_round_naive() {
let dt = Utc let dt = Utc
.ymd_opt(2016, 12, 31) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2016, 12, 31)
.and_hms_nano_opt(23, 59, 59, 175_500_000) .unwrap()
.and_hms_nano_opt(23, 59, 59, 175_500_000)
.unwrap(),
)
.unwrap() .unwrap()
.naive_utc(); .naive_utc();
@ -497,9 +551,12 @@ mod tests {
// round up // round up
let dt = Utc let dt = Utc
.ymd_opt(2012, 12, 12) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2012, 12, 12)
.and_hms_milli_opt(18, 22, 30, 0) .unwrap()
.and_hms_milli_opt(18, 22, 30, 0)
.unwrap(),
)
.unwrap() .unwrap()
.naive_utc(); .naive_utc();
assert_eq!( assert_eq!(
@ -508,9 +565,12 @@ mod tests {
); );
// round down // round down
let dt = Utc let dt = Utc
.ymd_opt(2012, 12, 12) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2012, 12, 12)
.and_hms_milli_opt(18, 22, 29, 999) .unwrap()
.and_hms_milli_opt(18, 22, 29, 999)
.unwrap(),
)
.unwrap() .unwrap()
.naive_utc(); .naive_utc();
assert_eq!( assert_eq!(
@ -538,7 +598,7 @@ mod tests {
#[test] #[test]
fn test_duration_round_pre_epoch() { fn test_duration_round_pre_epoch() {
let dt = Utc.ymd_opt(1969, 12, 12).unwrap().and_hms_opt(12, 12, 12).unwrap(); let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap();
assert_eq!( assert_eq!(
dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(), dt.duration_round(TimeDelta::minutes(10)).unwrap().to_string(),
"1969-12-12 12:10:00 UTC" "1969-12-12 12:10:00 UTC"
@ -547,8 +607,14 @@ mod tests {
#[test] #[test]
fn test_duration_trunc() { fn test_duration_trunc() {
let dt = let dt = Utc
Utc.ymd_opt(2016, 12, 31).unwrap().and_hms_nano_opt(23, 59, 59, 175_500_000).unwrap(); .from_local_datetime(
&NaiveDate::from_ymd_opt(2016, 12, 31)
.unwrap()
.and_hms_nano_opt(23, 59, 59, 175_500_000)
.unwrap(),
)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(), dt.duration_trunc(TimeDelta::milliseconds(10)).unwrap().to_string(),
@ -556,13 +622,27 @@ mod tests {
); );
// would round up // would round up
let dt = Utc.ymd_opt(2012, 12, 12).unwrap().and_hms_milli_opt(18, 22, 30, 0).unwrap(); let dt = Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2012, 12, 12)
.unwrap()
.and_hms_milli_opt(18, 22, 30, 0)
.unwrap(),
)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(), dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC" "2012-12-12 18:20:00 UTC"
); );
// would round down // would round down
let dt = Utc.ymd_opt(2012, 12, 12).unwrap().and_hms_milli_opt(18, 22, 29, 999).unwrap(); let dt = Utc
.from_local_datetime(
&NaiveDate::from_ymd_opt(2012, 12, 12)
.unwrap()
.and_hms_milli_opt(18, 22, 29, 999)
.unwrap(),
)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(), dt.duration_trunc(TimeDelta::minutes(5)).unwrap().to_string(),
"2012-12-12 18:20:00 UTC" "2012-12-12 18:20:00 UTC"
@ -585,12 +665,8 @@ mod tests {
); );
// timezone east // timezone east
let dt = FixedOffset::east_opt(3600) let dt =
.unwrap() FixedOffset::east_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
.ymd_opt(2020, 10, 27)
.unwrap()
.and_hms_opt(15, 0, 0)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(), dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-27 00:00:00 +01:00" "2020-10-27 00:00:00 +01:00"
@ -601,12 +677,8 @@ mod tests {
); );
// timezone west // timezone west
let dt = FixedOffset::west_opt(3600) let dt =
.unwrap() FixedOffset::west_opt(3600).unwrap().with_ymd_and_hms(2020, 10, 27, 15, 0, 0).unwrap();
.ymd_opt(2020, 10, 27)
.unwrap()
.and_hms_opt(15, 0, 0)
.unwrap();
assert_eq!( assert_eq!(
dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(), dt.duration_trunc(TimeDelta::days(1)).unwrap().to_string(),
"2020-10-27 00:00:00 -01:00" "2020-10-27 00:00:00 -01:00"
@ -620,9 +692,12 @@ mod tests {
#[test] #[test]
fn test_duration_trunc_naive() { fn test_duration_trunc_naive() {
let dt = Utc let dt = Utc
.ymd_opt(2016, 12, 31) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2016, 12, 31)
.and_hms_nano_opt(23, 59, 59, 175_500_000) .unwrap()
.and_hms_nano_opt(23, 59, 59, 175_500_000)
.unwrap(),
)
.unwrap() .unwrap()
.naive_utc(); .naive_utc();
@ -633,9 +708,12 @@ mod tests {
// would round up // would round up
let dt = Utc let dt = Utc
.ymd_opt(2012, 12, 12) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2012, 12, 12)
.and_hms_milli_opt(18, 22, 30, 0) .unwrap()
.and_hms_milli_opt(18, 22, 30, 0)
.unwrap(),
)
.unwrap() .unwrap()
.naive_utc(); .naive_utc();
assert_eq!( assert_eq!(
@ -644,9 +722,12 @@ mod tests {
); );
// would round down // would round down
let dt = Utc let dt = Utc
.ymd_opt(2012, 12, 12) .from_local_datetime(
.unwrap() &NaiveDate::from_ymd_opt(2012, 12, 12)
.and_hms_milli_opt(18, 22, 29, 999) .unwrap()
.and_hms_milli_opt(18, 22, 29, 999)
.unwrap(),
)
.unwrap() .unwrap()
.naive_utc(); .naive_utc();
assert_eq!( assert_eq!(
@ -673,7 +754,7 @@ mod tests {
#[test] #[test]
fn test_duration_trunc_pre_epoch() { fn test_duration_trunc_pre_epoch() {
let dt = Utc.ymd_opt(1969, 12, 12).unwrap().and_hms_opt(12, 12, 12).unwrap(); let dt = Utc.with_ymd_and_hms(1969, 12, 12, 12, 12, 12).unwrap();
assert_eq!( assert_eq!(
dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(), dt.duration_trunc(TimeDelta::minutes(10)).unwrap().to_string(),
"1969-12-12 12:10:00 UTC" "1969-12-12 12:10:00 UTC"

View File

@ -483,6 +483,24 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
(this / other, this % other) (this / other, this % other)
} }
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for TimeDelta {
fn arbitrary(u: &mut arbitrary::Unstructured) -> arbitrary::Result<TimeDelta> {
const MIN_SECS: i64 = i64::MIN / MILLIS_PER_SEC - 1;
const MAX_SECS: i64 = i64::MAX / MILLIS_PER_SEC;
let secs: i64 = u.int_in_range(MIN_SECS..=MAX_SECS)?;
let nanos: i32 = u.int_in_range(0..=(NANOS_PER_SEC - 1))?;
let duration = TimeDelta { secs, nanos };
if duration < MIN || duration > MAX {
Err(arbitrary::Error::IncorrectFormat)
} else {
Ok(duration)
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{OutOfRangeError, TimeDelta, MAX, MIN}; use super::{OutOfRangeError, TimeDelta, MAX, MIN};

View File

@ -12,6 +12,7 @@ use crate::OutOfRange;
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result. /// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)] #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))] #[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum Weekday { pub enum Weekday {
/// Monday. /// Monday.
Mon = 0, Mon = 0,

View File

@ -19,7 +19,7 @@ fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) {
// seems to be consistent with the output of the `date` command, so we simply // seems to be consistent with the output of the `date` command, so we simply
// compare both. // compare both.
// let local = Local // let local = Local
// .from_local_datetime(&NaiveDate::from_ymd_opt(year, month, day).unwrap().and_hms_opt(hour, 5, 1).unwrap()) // .with_ymd_and_hms(year, month, day, hour, 5, 1)
// // looks like the "date" command always returns a given time when it is ambiguous // // looks like the "date" command always returns a given time when it is ambiguous
// .earliest(); // .earliest();