Merge branch '0.4.x' into main

This commit is contained in:
Paul Dicker 2023-05-11 17:28:07 +02:00
commit 7926f011be
32 changed files with 506 additions and 305 deletions

View File

@ -32,9 +32,8 @@ jobs:
- run: cargo test --doc --all-features --color=always -- --color=always
# later this may be able to be included with the below
# kept seperate for now as the following don't compile on 1.38.0
# * rkyv
# * criterion
# kept separate for now as the following don't compile on 1.56.1
# * arbitrary
rust_msrv:
strategy:
matrix:
@ -44,7 +43,7 @@ jobs:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.48.0
toolchain: 1.56.1
- uses: Swatinem/rust-cache@v2
# run --lib and --doc to avoid the long running integration tests which are run elsewhere
- run: cargo test --lib --features unstable-locales,wasmbind,clock,serde,windows-sys --color=always -- --color=always

View File

@ -11,6 +11,7 @@ readme = "README.md"
license = "MIT OR Apache-2.0"
exclude = ["/ci/*"]
edition = "2018"
rust-version = "1.56.0"
[lib]
name = "chrono"
@ -38,7 +39,7 @@ wasm-bindgen = { version = "0.2", optional = true }
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API
[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_System_Time", "Win32_Foundation"], optional = true }
windows-sys = { version = "0.48.0", features = ["Win32_System_Time", "Win32_System_SystemInformation", "Win32_Foundation"], optional = true }
[target.'cfg(unix)'.dependencies]
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }

View File

@ -64,7 +64,7 @@ fn bench_year_flags_from_year(c: &mut Criterion) {
c.bench_function("bench_year_flags_from_year", |b| {
b.iter(|| {
for year in -999i32..1000 {
__BenchYearFlags::from_year(year);
let _ = __BenchYearFlags::from_year(black_box(year));
}
})
});

View File

@ -1 +0,0 @@
msrv = "1.48"

View File

@ -77,6 +77,7 @@ impl<Tz: TimeZone> Date<Tz> {
//
// note: this constructor is purposely not named to `new` to discourage the direct usage.
#[inline]
#[must_use]
pub fn from_utc(date: NaiveDate, offset: Tz::Offset) -> Date<Tz> {
Date { date, offset }
}
@ -86,6 +87,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Panics on invalid datetime.
#[inline]
#[must_use]
pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Tz>> {
let localdt = self.naive_local().and_time(time);
self.timezone().from_local_datetime(&localdt).single()
@ -97,6 +99,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Panics on invalid hour, minute and/or second.
#[deprecated(since = "0.4.23", note = "Use and_hms_opt() instead")]
#[inline]
#[must_use]
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Tz> {
self.and_hms_opt(hour, min, sec).expect("invalid time")
}
@ -106,6 +109,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` on invalid hour, minute and/or second.
#[inline]
#[must_use]
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Tz>> {
NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
}
@ -117,6 +121,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Panics on invalid hour, minute, second and/or millisecond.
#[deprecated(since = "0.4.23", note = "Use and_hms_milli_opt() instead")]
#[inline]
#[must_use]
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Tz> {
self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
}
@ -127,6 +132,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` on invalid hour, minute, second and/or millisecond.
#[inline]
#[must_use]
pub fn and_hms_milli_opt(
&self,
hour: u32,
@ -144,6 +150,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Panics on invalid hour, minute, second and/or microsecond.
#[deprecated(since = "0.4.23", note = "Use and_hms_micro_opt() instead")]
#[inline]
#[must_use]
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Tz> {
self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
}
@ -154,6 +161,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` on invalid hour, minute, second and/or microsecond.
#[inline]
#[must_use]
pub fn and_hms_micro_opt(
&self,
hour: u32,
@ -171,6 +179,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Panics on invalid hour, minute, second and/or nanosecond.
#[deprecated(since = "0.4.23", note = "Use and_hms_nano_opt() instead")]
#[inline]
#[must_use]
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Tz> {
self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
}
@ -181,6 +190,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` on invalid hour, minute, second and/or nanosecond.
#[inline]
#[must_use]
pub fn and_hms_nano_opt(
&self,
hour: u32,
@ -196,6 +206,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Panics when `self` is the last representable date.
#[deprecated(since = "0.4.23", note = "Use succ_opt() instead")]
#[inline]
#[must_use]
pub fn succ(&self) -> Date<Tz> {
self.succ_opt().expect("out of bound")
}
@ -204,6 +215,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when `self` is the last representable date.
#[inline]
#[must_use]
pub fn succ_opt(&self) -> Option<Date<Tz>> {
self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
}
@ -213,6 +225,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Panics when `self` is the first representable date.
#[deprecated(since = "0.4.23", note = "Use pred_opt() instead")]
#[inline]
#[must_use]
pub fn pred(&self) -> Date<Tz> {
self.pred_opt().expect("out of bound")
}
@ -221,18 +234,21 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when `self` is the first representable date.
#[inline]
#[must_use]
pub fn pred_opt(&self) -> Option<Date<Tz>> {
self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
}
/// Retrieves an associated offset from UTC.
#[inline]
#[must_use]
pub fn offset(&self) -> &Tz::Offset {
&self.offset
}
/// Retrieves an associated time zone.
#[inline]
#[must_use]
pub fn timezone(&self) -> Tz {
TimeZone::from_offset(&self.offset)
}
@ -240,6 +256,7 @@ impl<Tz: TimeZone> Date<Tz> {
/// Changes the associated time zone.
/// This does not change the actual `Date` (but will change the string representation).
#[inline]
#[must_use]
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> Date<Tz2> {
tz.from_utc_date(&self.date)
}
@ -248,6 +265,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
#[must_use]
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<Date<Tz>> {
let date = self.date.checked_add_signed(rhs)?;
Some(Date { date, offset: self.offset })
@ -257,6 +275,7 @@ impl<Tz: TimeZone> Date<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
#[must_use]
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<Date<Tz>> {
let date = self.date.checked_sub_signed(rhs)?;
Some(Date { date, offset: self.offset })
@ -268,12 +287,14 @@ impl<Tz: TimeZone> Date<Tz> {
/// This does not overflow or underflow at all,
/// as all possible output fits in the range of `Duration`.
#[inline]
#[must_use]
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: Date<Tz2>) -> TimeDelta {
self.date.signed_duration_since(rhs.date)
}
/// Returns a view to the naive UTC date.
#[inline]
#[must_use]
pub fn naive_utc(&self) -> NaiveDate {
self.date
}
@ -284,11 +305,13 @@ impl<Tz: TimeZone> Date<Tz> {
/// because the offset is restricted to never exceed one day,
/// but provided for the consistency.
#[inline]
#[must_use]
pub fn naive_local(&self) -> NaiveDate {
self.date
}
/// Returns the number of whole years from the given `base` until `self`.
#[must_use]
pub fn years_since(&self, base: Self) -> Option<u32> {
self.date.years_since(base.date)
}
@ -315,6 +338,7 @@ where
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
@ -329,6 +353,7 @@ where
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}
@ -337,6 +362,7 @@ where
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[inline]
#[must_use]
pub fn format_localized_with_items<'a, I, B>(
&self,
items: I,
@ -361,6 +387,7 @@ where
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[inline]
#[must_use]
pub fn format_localized<'a>(
&self,
fmt: &'a str,

View File

@ -105,6 +105,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
//
// note: this constructor is purposely not named to `new` to discourage the direct usage.
#[inline]
#[must_use]
pub fn from_utc(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
DateTime { datetime, offset }
}
@ -134,6 +135,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// assert_eq!(datetime_west, datetime_utc.with_timezone(&timezone_west));
/// ```
#[inline]
#[must_use]
pub fn from_local(datetime: NaiveDateTime, offset: Tz::Offset) -> DateTime<Tz> {
let datetime_utc = datetime - offset.fix();
@ -148,6 +150,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
#[inline]
#[deprecated(since = "0.4.23", note = "Use `date_naive()` instead")]
#[allow(deprecated)]
#[must_use]
pub fn date(&self) -> Date<Tz> {
Date::from_utc(self.naive_local().date(), self.offset.clone())
}
@ -165,6 +168,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// assert_eq!(date.date_naive(), other.date_naive());
/// ```
#[inline]
#[must_use]
pub fn date_naive(&self) -> NaiveDate {
let local = self.naive_local();
NaiveDate::from_ymd_opt(local.year(), local.month(), local.day()).unwrap()
@ -173,6 +177,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Retrieves a time component.
/// Unlike `date`, this is not associated to the time zone.
#[inline]
#[must_use]
pub fn time(&self) -> NaiveTime {
self.datetime.time() + self.offset.fix()
}
@ -180,6 +185,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC
/// (aka "UNIX timestamp").
#[inline]
#[must_use]
pub fn timestamp(&self) -> i64 {
self.datetime.timestamp()
}
@ -203,6 +209,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// assert_eq!(dt.timestamp_millis(), 1_000_000_000_555);
/// ```
#[inline]
#[must_use]
pub fn timestamp_millis(&self) -> i64 {
self.datetime.timestamp_millis()
}
@ -226,6 +233,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555);
/// ```
#[inline]
#[must_use]
pub fn timestamp_micros(&self) -> i64 {
self.datetime.timestamp_micros()
}
@ -249,6 +257,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// assert_eq!(dt.timestamp_nanos(), 1_000_000_000_000_000_555);
/// ```
#[inline]
#[must_use]
pub fn timestamp_nanos(&self) -> i64 {
self.datetime.timestamp_nanos()
}
@ -259,6 +268,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// note: this is not the number of milliseconds since January 1, 1970 0:00:00 UTC
#[inline]
#[must_use]
pub fn timestamp_subsec_millis(&self) -> u32 {
self.datetime.timestamp_subsec_millis()
}
@ -269,6 +279,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// note: this is not the number of microseconds since January 1, 1970 0:00:00 UTC
#[inline]
#[must_use]
pub fn timestamp_subsec_micros(&self) -> u32 {
self.datetime.timestamp_subsec_micros()
}
@ -279,18 +290,21 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// note: this is not the number of nanoseconds since January 1, 1970 0:00:00 UTC
#[inline]
#[must_use]
pub fn timestamp_subsec_nanos(&self) -> u32 {
self.datetime.timestamp_subsec_nanos()
}
/// Retrieves an associated offset from UTC.
#[inline]
#[must_use]
pub fn offset(&self) -> &Tz::Offset {
&self.offset
}
/// Retrieves an associated time zone.
#[inline]
#[must_use]
pub fn timezone(&self) -> Tz {
TimeZone::from_offset(&self.offset)
}
@ -298,6 +312,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Changes the associated time zone.
/// The returned `DateTime` references the same instant of time from the perspective of the provided time zone.
#[inline]
#[must_use]
pub fn with_timezone<Tz2: TimeZone>(&self, tz: &Tz2) -> DateTime<Tz2> {
tz.from_utc_datetime(&self.datetime)
}
@ -306,6 +321,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
#[must_use]
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<DateTime<Tz>> {
let datetime = self.datetime.checked_add_signed(rhs)?;
let tz = self.timezone();
@ -318,6 +334,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// local time is not valid on the newly calculated date.
///
/// See [`NaiveDate::checked_add_months`] for more details on behavior
#[must_use]
pub fn checked_add_months(self, rhs: Months) -> Option<DateTime<Tz>> {
self.naive_local()
.checked_add_months(rhs)?
@ -329,6 +346,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
///
/// Returns `None` when it will result in overflow.
#[inline]
#[must_use]
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<DateTime<Tz>> {
let datetime = self.datetime.checked_sub_signed(rhs)?;
let tz = self.timezone();
@ -341,6 +359,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// local time is not valid on the newly calculated date.
///
/// See [`NaiveDate::checked_sub_months`] for more details on behavior
#[must_use]
pub fn checked_sub_months(self, rhs: Months) -> Option<DateTime<Tz>> {
self.naive_local()
.checked_sub_months(rhs)?
@ -351,6 +370,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Add a duration in [`Days`] to the date part of the `DateTime`
///
/// Returns `None` if the resulting date would be out of range.
#[must_use]
pub fn checked_add_days(self, days: Days) -> Option<Self> {
self.naive_local()
.checked_add_days(days)?
@ -361,6 +381,7 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Subtract a duration in [`Days`] from the date part of the `DateTime`
///
/// Returns `None` if the resulting date would be out of range.
#[must_use]
pub fn checked_sub_days(self, days: Days) -> Option<Self> {
self.naive_local()
.checked_sub_days(days)?
@ -371,23 +392,27 @@ impl<Tz: TimeZone> DateTime<Tz> {
/// Subtracts another `DateTime` from the current date and time.
/// This does not overflow or underflow at all.
#[inline]
#[must_use]
pub fn signed_duration_since<Tz2: TimeZone>(self, rhs: DateTime<Tz2>) -> TimeDelta {
self.datetime.signed_duration_since(rhs.datetime)
}
/// Returns a view to the naive UTC datetime.
#[inline]
#[must_use]
pub fn naive_utc(&self) -> NaiveDateTime {
self.datetime
}
/// Returns a view to the naive local datetime.
#[inline]
#[must_use]
pub fn naive_local(&self) -> NaiveDateTime {
self.datetime + self.offset.fix()
}
/// Retrieve the elapsed years from now to the given [`DateTime`].
#[must_use]
pub fn years_since(&self, base: Self) -> Option<u32> {
let mut years = self.year() - base.year();
let earlier_time =
@ -648,6 +673,7 @@ where
/// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`.
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc2822(&self) -> String {
let mut result = String::with_capacity(32);
crate::format::write_rfc2822(&mut result, self.naive_local(), self.offset.fix())
@ -658,6 +684,7 @@ where
/// 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_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc3339(&self) -> String {
let mut result = String::with_capacity(32);
crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix())
@ -691,6 +718,7 @@ where
/// ```
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String {
use crate::format::Numeric::*;
use crate::format::Pad::Zero;
@ -734,6 +762,7 @@ where
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
@ -758,6 +787,7 @@ where
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}
@ -766,6 +796,7 @@ where
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[inline]
#[must_use]
pub fn format_localized_with_items<'a, I, B>(
&self,
items: I,
@ -793,6 +824,7 @@ where
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[inline]
#[must_use]
pub fn format_localized<'a>(
&self,
fmt: &'a str,

View File

@ -158,6 +158,7 @@ pub mod ts_nanoseconds {
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -184,6 +185,7 @@ pub mod ts_nanoseconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where
D: de::Deserializer<'de>,
@ -282,6 +284,7 @@ pub mod ts_nanoseconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -311,6 +314,7 @@ pub mod ts_nanoseconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: de::Deserializer<'de>,
@ -411,6 +415,7 @@ pub mod ts_microseconds {
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -437,6 +442,7 @@ pub mod ts_microseconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where
D: de::Deserializer<'de>,
@ -534,6 +540,7 @@ pub mod ts_microseconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -563,6 +570,7 @@ pub mod ts_microseconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: de::Deserializer<'de>,
@ -663,6 +671,7 @@ pub mod ts_milliseconds {
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -689,6 +698,7 @@ pub mod ts_milliseconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where
D: de::Deserializer<'de>,
@ -783,6 +793,7 @@ pub mod ts_milliseconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -824,6 +835,7 @@ pub mod ts_milliseconds_option {
/// assert_eq!(t, E::V(S { time: None }));
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: de::Deserializer<'de>,
@ -925,6 +937,7 @@ pub mod ts_seconds {
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -951,6 +964,7 @@ pub mod ts_seconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where
D: de::Deserializer<'de>,
@ -1042,6 +1056,7 @@ pub mod ts_seconds_option {
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<DateTime<Utc>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -1071,6 +1086,7 @@ pub mod ts_seconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: de::Deserializer<'de>,

View File

@ -1873,3 +1873,38 @@ fn test_datetime_sub_assign_local() {
assert_eq!(datetime_sub, datetime - TimeDelta::days(i))
}
}
#[test]
#[cfg(target_os = "windows")]
fn test_from_naive_date_time_windows() {
let min_year = NaiveDate::from_ymd_opt(1601, 1, 3).unwrap().and_hms_opt(0, 0, 0).unwrap();
let max_year = NaiveDate::from_ymd_opt(30827, 12, 29).unwrap().and_hms_opt(23, 59, 59).unwrap();
let too_low_year =
NaiveDate::from_ymd_opt(1600, 12, 29).unwrap().and_hms_opt(23, 59, 59).unwrap();
let too_high_year = NaiveDate::from_ymd_opt(30829, 1, 3).unwrap().and_hms_opt(0, 0, 0).unwrap();
let _ = Local.from_utc_datetime(&min_year);
let _ = Local.from_utc_datetime(&max_year);
let _ = Local.from_local_datetime(&min_year);
let _ = Local.from_local_datetime(&max_year);
let local_too_low = Local.from_local_datetime(&too_low_year);
let local_too_high = Local.from_local_datetime(&too_high_year);
assert_eq!(local_too_low, LocalResult::None);
assert_eq!(local_too_high, LocalResult::None);
let err = std::panic::catch_unwind(|| {
Local.from_utc_datetime(&too_low_year);
});
assert!(err.is_err());
let err = std::panic::catch_unwind(|| {
Local.from_utc_datetime(&too_high_year);
});
assert!(err.is_err());
}

View File

@ -863,6 +863,7 @@ pub struct DelayedFormat<I> {
#[cfg(any(feature = "alloc", feature = "std", test))]
impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
/// Makes a new `DelayedFormat` value out of local date and time.
#[must_use]
pub fn new(date: Option<NaiveDate>, time: Option<NaiveTime>, items: I) -> DelayedFormat<I> {
DelayedFormat {
date,
@ -875,6 +876,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
}
/// Makes a new `DelayedFormat` value out of local date and time and UTC offset.
#[must_use]
pub fn new_with_offset<Off>(
date: Option<NaiveDate>,
time: Option<NaiveTime>,
@ -898,6 +900,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
/// Makes a new `DelayedFormat` value out of local date and time and locale.
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[must_use]
pub fn new_with_locale(
date: Option<NaiveDate>,
time: Option<NaiveTime>,
@ -910,6 +913,7 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
/// Makes a new `DelayedFormat` value out of local date and time, UTC offset and locale.
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[must_use]
pub fn new_with_offset_and_locale<Off>(
date: Option<NaiveDate>,
time: Option<NaiveTime>,

View File

@ -1377,6 +1377,28 @@ fn test_rfc2822() {
("Tue, 20 Jan 2015 17:35:20 -0890", Err(OUT_OF_RANGE)), // bad offset
("6 Jun 1944 04:00:00Z", Err(INVALID)), // bad offset (zulu not allowed)
("Tue, 20 Jan 2015 17:35:20 HAS", Err(NOT_ENOUGH)), // bad named time zone
// named timezones that have specific timezone offsets
// see https://www.rfc-editor.org/rfc/rfc2822#section-4.3
("Tue, 20 Jan 2015 17:35:20 GMT", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 UT", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 ut", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 EDT", Ok("Tue, 20 Jan 2015 17:35:20 -0400")),
("Tue, 20 Jan 2015 17:35:20 EST", Ok("Tue, 20 Jan 2015 17:35:20 -0500")),
("Tue, 20 Jan 2015 17:35:20 CDT", Ok("Tue, 20 Jan 2015 17:35:20 -0500")),
("Tue, 20 Jan 2015 17:35:20 CST", Ok("Tue, 20 Jan 2015 17:35:20 -0600")),
("Tue, 20 Jan 2015 17:35:20 MDT", Ok("Tue, 20 Jan 2015 17:35:20 -0600")),
("Tue, 20 Jan 2015 17:35:20 MST", Ok("Tue, 20 Jan 2015 17:35:20 -0700")),
("Tue, 20 Jan 2015 17:35:20 PDT", Ok("Tue, 20 Jan 2015 17:35:20 -0700")),
("Tue, 20 Jan 2015 17:35:20 PST", Ok("Tue, 20 Jan 2015 17:35:20 -0800")),
("Tue, 20 Jan 2015 17:35:20 pst", Ok("Tue, 20 Jan 2015 17:35:20 -0800")),
// named single-letter military timezones must fallback to +0000
("Tue, 20 Jan 2015 17:35:20 Z", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 A", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 a", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 K", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
("Tue, 20 Jan 2015 17:35:20 k", Ok("Tue, 20 Jan 2015 17:35:20 +0000")),
// named single-letter timezone "J" is specifically not valid
("Tue, 20 Jan 2015 17:35:20 J", Err(NOT_ENOUGH)),
("Tue, 20 Jan 2015😈17:35:20 -0800", Err(INVALID)), // bad character!
];
@ -1515,3 +1537,11 @@ fn test_rfc3339() {
}
}
}
#[cfg(test)]
#[test]
fn test_issue_1010() {
let dt = crate::NaiveDateTime::parse_from_str("\u{c}SUN\u{e}\u{3000}\0m@J\u{3000}\0\u{3000}\0m\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}\0SU\u{c}\u{c}",
"\u{c}\u{c}%A\u{c}\u{b}\0SUN\u{c}\u{c}\u{c}SUNN\u{c}\u{c}\u{c}SUN\u{c}\u{c}!\u{c}\u{b}\u{c}\u{c}\u{c}\u{c}%A\u{c}\u{b}%a");
assert_eq!(dt, Err(ParseError(ParseErrorKind::Invalid)));
}

View File

@ -122,6 +122,7 @@ fn set_if_consistent<T: PartialEq>(old: &mut Option<T>, new: T) -> ParseResult<(
impl Parsed {
/// Returns the initial value of parsed parts.
#[must_use]
pub fn new() -> Parsed {
Parsed::default()
}
@ -1287,4 +1288,18 @@ mod tests {
// TODO test with a variable time zone (for None and Ambiguous cases)
}
#[test]
fn issue_551() {
use crate::Weekday;
let mut parsed = Parsed::new();
parsed.year = Some(2002);
parsed.week_from_mon = Some(22);
parsed.weekday = Some(Weekday::Mon);
assert_eq!(NaiveDate::from_ymd_opt(2002, 6, 3).unwrap(), parsed.to_naive_date().unwrap());
parsed.year = Some(2001);
assert_eq!(NaiveDate::from_ymd_opt(2001, 5, 28).unwrap(), parsed.to_naive_date().unwrap());
}
}

View File

@ -12,8 +12,8 @@ use crate::Weekday;
/// Returns true when two slices are equal case-insensitively (in ASCII).
/// Assumes that the `pattern` is already converted to lower case.
fn equals(s: &str, pattern: &str) -> bool {
let mut xs = s.as_bytes().iter().map(|&c| match c {
fn equals(s: &[u8], pattern: &str) -> bool {
let mut xs = s.iter().map(|&c| match c {
b'A'..=b'Z' => c + 32,
_ => c,
});
@ -152,7 +152,7 @@ pub(super) fn short_or_long_month0(s: &str) -> ParseResult<(&str, u8)> {
// tries to consume the suffix if possible
let suffix = LONG_MONTH_SUFFIXES[month0 as usize];
if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
if s.len() >= suffix.len() && equals(&s.as_bytes()[..suffix.len()], suffix) {
s = &s[suffix.len()..];
}
@ -170,7 +170,7 @@ pub(super) fn short_or_long_weekday(s: &str) -> ParseResult<(&str, Weekday)> {
// tries to consume the suffix if possible
let suffix = LONG_WEEKDAY_SUFFIXES[weekday.num_days_from_monday() as usize];
if s.len() >= suffix.len() && equals(&s[..suffix.len()], suffix) {
if s.len() >= suffix.len() && equals(&s.as_bytes()[..suffix.len()], suffix) {
s = &s[suffix.len()..];
}
@ -340,11 +340,15 @@ where
/// Same as `timezone_offset` but also allows for RFC 2822 legacy timezones.
/// May return `None` which indicates an insufficient offset data (i.e. `-0000`).
/// See [RFC 2822 Section 4.3].
///
/// [RFC 2822 Section 4.3]: https://tools.ietf.org/html/rfc2822#section-4.3
pub(super) fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)> {
// tries to parse legacy time zone names
let upto = s.as_bytes().iter().position(|c| !c.is_ascii_alphabetic()).unwrap_or(s.len());
let upto = s.as_bytes().iter().position(|&c| !c.is_ascii_alphabetic()).unwrap_or(s.len());
if upto > 0 {
let (name, s) = s.split_at(upto);
let name = &s.as_bytes()[..upto];
let s = &s[upto..];
let offset_hours = |o| Ok((s, Some(o * 3600)));
if equals(name, "gmt") || equals(name, "ut") {
offset_hours(0)
@ -358,8 +362,14 @@ pub(super) fn timezone_offset_2822(s: &str) -> ParseResult<(&str, Option<i32>)>
offset_hours(-7)
} else if equals(name, "pst") {
offset_hours(-8)
} else if name.len() == 1 {
match name[0] {
// recommended by RFC 2822: consume but treat it as -0000
b'a'..=b'i' | b'k'..=b'z' | b'A'..=b'I' | b'K'..=b'Z' => offset_hours(0),
_ => Ok((s, None)),
}
} else {
Ok((s, None)) // recommended by RFC 2822: consume but treat it as -0000
Ok((s, None))
}
} else {
let (s_, offset) = timezone_offset(s, |s| Ok(s))?;

View File

@ -68,7 +68,7 @@ The following specifiers are available both to formatting and parsing.
| `%r` | `12:34:60 AM` | Hour-minute-second format in 12-hour clocks. Same as `%I:%M:%S %p`. |
| | | |
| | | **TIME ZONE SPECIFIERS:** |
| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. [^8] |
| `%Z` | `ACST` | Local time zone name. Skips all non-whitespace characters during parsing. Identical to `%:z` when formatting. [^8] |
| `%z` | `+0930` | Offset from the local time to UTC (with UTC being `+0000`). |
| `%:z` | `+09:30` | Same as `%z` but with a colon. |
|`%::z`|`+09:30:00`| Offset from the local time to UTC with seconds. |
@ -164,6 +164,12 @@ Notes:
Note that they can read nothing if the fractional part is zero.
[^8]: `%Z`:
Since `chrono` is not aware of timezones beyond their offsets, this specifier
**only prints the offset** when used for formatting. The timezone abbreviation
will NOT be printed. See [this issue](https://github.com/chronotope/chrono/issues/960)
for more information.
<br>
<br>
Offset will not be populated from the parsed data, nor will it be validated.
Timezone is completely ignored. Similar to the glibc `strptime` treatment of
this format code.
@ -227,6 +233,7 @@ pub struct StrftimeItems<'a> {
impl<'a> StrftimeItems<'a> {
/// Creates a new parsing iterator from the `strftime`-like format string.
#[must_use]
pub fn new(s: &'a str) -> StrftimeItems<'a> {
Self::with_remainer(s)
}
@ -234,6 +241,7 @@ impl<'a> StrftimeItems<'a> {
/// Creates a new parsing iterator from the `strftime`-like format string.
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[must_use]
pub fn new_with_locale(s: &'a str, locale: Locale) -> StrftimeItems<'a> {
let d_fmt = StrftimeItems::new(locales::d_fmt(locale)).collect();
let d_t_fmt = StrftimeItems::new(locales::d_t_fmt(locale)).collect();

View File

@ -519,15 +519,3 @@ impl fmt::Debug for OutOfRange {
#[cfg(feature = "std")]
impl std::error::Error for OutOfRange {}
/// MSRV 1.42
#[cfg(test)]
#[macro_export]
macro_rules! matches {
($expression:expr, $(|)? $( $pattern:pat )|+ $( if $guard: expr )? $(,)?) => {
match $expression {
$( $pattern )|+ $( if $guard )? => true,
_ => false
}
}
}

View File

@ -29,7 +29,7 @@ use crate::OutOfRange;
/// Allows mapping from and to month, from 1-January to 12-December.
/// Can be Serialized/Deserialized with serde
// Actual implementation is zero-indexed, API intended as 1-indexed for more intuitive behavior.
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum Month {
@ -66,6 +66,7 @@ impl Month {
/// ----------- | --------- | ---------- | --- | ---------
/// `m.succ()`: | `February` | `March` | `...` | `January`
#[inline]
#[must_use]
pub const fn succ(&self) -> Month {
match *self {
Month::January => Month::February,
@ -89,6 +90,7 @@ impl Month {
/// ----------- | --------- | ---------- | --- | ---------
/// `m.pred()`: | `December` | `January` | `...` | `November`
#[inline]
#[must_use]
pub const fn pred(&self) -> Month {
match *self {
Month::January => Month::December,
@ -112,6 +114,7 @@ impl Month {
/// -------------------------| --------- | ---------- | --- | -----
/// `m.number_from_month()`: | 1 | 2 | `...` | 12
#[inline]
#[must_use]
pub const fn number_from_month(&self) -> u32 {
match *self {
Month::January => 1,
@ -136,6 +139,7 @@ impl Month {
///
/// assert_eq!(Month::January.name(), "January")
/// ```
#[must_use]
pub const fn name(&self) -> &'static str {
match *self {
Month::January => "January",
@ -334,4 +338,13 @@ mod tests {
assert_eq!(Month::January.pred(), Month::December);
assert_eq!(Month::February.pred(), Month::January);
}
#[test]
fn test_month_partial_ord() {
assert!(Month::January <= Month::January);
assert!(Month::January < Month::February);
assert!(Month::January < Month::December);
assert!(Month::July >= Month::May);
assert!(Month::September > Month::March);
}
}

View File

@ -75,6 +75,7 @@ impl NaiveWeek {
/// assert!(week.first_day() <= date);
/// ```
#[inline]
#[must_use]
pub fn first_day(&self) -> NaiveDate {
let start = self.start.num_days_from_monday();
let end = self.date.weekday().num_days_from_monday();
@ -94,6 +95,7 @@ impl NaiveWeek {
/// assert!(week.last_day() >= date);
/// ```
#[inline]
#[must_use]
pub fn last_day(&self) -> NaiveDate {
self.first_day() + TimeDelta::days(6)
}
@ -113,6 +115,7 @@ impl NaiveWeek {
/// assert!(days.contains(&date));
/// ```
#[inline]
#[must_use]
pub fn days(&self) -> RangeInclusive<NaiveDate> {
self.first_day()..=self.last_day()
}
@ -257,6 +260,7 @@ impl NaiveDate {
///
/// Panics on the out-of-range date, invalid month and/or day.
#[deprecated(since = "0.4.23", note = "use `from_ymd_opt()` instead")]
#[must_use]
pub fn from_ymd(year: i32, month: u32, day: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(year, month, day).expect("invalid or out-of-range date")
}
@ -280,6 +284,7 @@ impl NaiveDate {
/// assert!(from_ymd_opt(400000, 1, 1).is_none());
/// assert!(from_ymd_opt(-400000, 1, 1).is_none());
/// ```
#[must_use]
pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<NaiveDate> {
let flags = YearFlags::from_year(year);
NaiveDate::from_mdf(year, Mdf::new(month, day, flags)?)
@ -290,6 +295,7 @@ impl NaiveDate {
///
/// Panics on the out-of-range date and/or invalid day of year.
#[deprecated(since = "0.4.23", note = "use `from_yo_opt()` instead")]
#[must_use]
pub fn from_yo(year: i32, ordinal: u32) -> NaiveDate {
NaiveDate::from_yo_opt(year, ordinal).expect("invalid or out-of-range date")
}
@ -314,6 +320,7 @@ impl NaiveDate {
/// assert!(from_yo_opt(400000, 1).is_none());
/// assert!(from_yo_opt(-400000, 1).is_none());
/// ```
#[must_use]
pub fn from_yo_opt(year: i32, ordinal: u32) -> Option<NaiveDate> {
let flags = YearFlags::from_year(year);
NaiveDate::from_of(year, Of::new(ordinal, flags)?)
@ -325,6 +332,7 @@ impl NaiveDate {
///
/// Panics on the out-of-range date and/or invalid week number.
#[deprecated(since = "0.4.23", note = "use `from_isoywd_opt()` instead")]
#[must_use]
pub fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> NaiveDate {
NaiveDate::from_isoywd_opt(year, week, weekday).expect("invalid or out-of-range date")
}
@ -373,6 +381,7 @@ impl NaiveDate {
/// assert_eq!(from_isoywd_opt(2015, 54, Weekday::Mon), None);
/// assert_eq!(from_isoywd_opt(2016, 1, Weekday::Mon), Some(from_ymd(2016, 1, 4)));
/// ```
#[must_use]
pub fn from_isoywd_opt(year: i32, week: u32, weekday: Weekday) -> Option<NaiveDate> {
let flags = YearFlags::from_year(year);
let nweeks = flags.nisoweeks();
@ -410,6 +419,7 @@ impl NaiveDate {
/// Panics if the date is out of range.
#[deprecated(since = "0.4.23", note = "use `from_num_days_from_ce_opt()` instead")]
#[inline]
#[must_use]
pub fn from_num_days_from_ce(days: i32) -> NaiveDate {
NaiveDate::from_num_days_from_ce_opt(days).expect("out-of-range date")
}
@ -434,8 +444,9 @@ impl NaiveDate {
/// assert_eq!(from_ndays_opt(100_000_000), None);
/// assert_eq!(from_ndays_opt(-100_000_000), None);
/// ```
#[must_use]
pub fn from_num_days_from_ce_opt(days: i32) -> Option<NaiveDate> {
let days = days + 365; // make December 31, 1 BCE equal to day 0
let days = days.checked_add(365)?; // make December 31, 1 BCE equal to day 0
let (year_div_400, cycle) = div_mod_floor(days, 146_097);
let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32);
let flags = YearFlags::from_year_mod_400(year_mod_400 as i32);
@ -453,6 +464,7 @@ impl NaiveDate {
///
/// `n` is 1-indexed. Passing `n=0` will cause a panic.
#[deprecated(since = "0.4.23", note = "use `from_weekday_of_month_opt()` instead")]
#[must_use]
pub fn from_weekday_of_month(year: i32, month: u32, weekday: Weekday, n: u8) -> NaiveDate {
NaiveDate::from_weekday_of_month_opt(year, month, weekday, n).expect("out-of-range date")
}
@ -469,6 +481,7 @@ impl NaiveDate {
///
/// Returns `None` if `n` out-of-range; ie. if `n` is larger than the number of `weekday` in
/// `month` (eg. the 6th Friday of March 2017), or if `n == 0`.
#[must_use]
pub fn from_weekday_of_month_opt(
year: i32,
month: u32,
@ -549,6 +562,7 @@ impl NaiveDate {
/// Some(NaiveDate::from_ymd_opt(2022, 9, 30).unwrap())
/// );
/// ```
#[must_use]
pub fn checked_add_months(self, months: Months) -> Option<Self> {
if months.0 == 0 {
return Some(self);
@ -579,6 +593,7 @@ impl NaiveDate {
/// None
/// );
/// ```
#[must_use]
pub fn checked_sub_months(self, months: Months) -> Option<Self> {
if months.0 == 0 {
return Some(self);
@ -652,6 +667,7 @@ impl NaiveDate {
/// None
/// );
/// ```
#[must_use]
pub fn checked_add_days(self, days: Days) -> Option<Self> {
if days.0 == 0 {
return Some(self);
@ -675,6 +691,7 @@ impl NaiveDate {
/// None
/// );
/// ```
#[must_use]
pub fn checked_sub_days(self, days: Days) -> Option<Self> {
if days.0 == 0 {
return Some(self);
@ -706,6 +723,7 @@ impl NaiveDate {
/// assert_eq!(dt.time(), t);
/// ```
#[inline]
#[must_use]
pub const fn and_time(&self, time: NaiveTime) -> NaiveDateTime {
NaiveDateTime::new(*self, time)
}
@ -718,6 +736,7 @@ impl NaiveDate {
/// Panics on invalid hour, minute and/or second.
#[deprecated(since = "0.4.23", note = "use `and_hms_opt()` instead")]
#[inline]
#[must_use]
pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> NaiveDateTime {
self.and_hms_opt(hour, min, sec).expect("invalid time")
}
@ -741,6 +760,7 @@ impl NaiveDate {
/// assert!(d.and_hms_opt(24, 34, 56).is_none());
/// ```
#[inline]
#[must_use]
pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<NaiveDateTime> {
NaiveTime::from_hms_opt(hour, min, sec).map(|time| self.and_time(time))
}
@ -753,6 +773,7 @@ impl NaiveDate {
/// Panics on invalid hour, minute, second and/or millisecond.
#[deprecated(since = "0.4.23", note = "use `and_hms_milli_opt()` instead")]
#[inline]
#[must_use]
pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> NaiveDateTime {
self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
}
@ -778,6 +799,7 @@ impl NaiveDate {
/// assert!(d.and_hms_milli_opt(24, 34, 56, 789).is_none());
/// ```
#[inline]
#[must_use]
pub fn and_hms_milli_opt(
&self,
hour: u32,
@ -810,6 +832,7 @@ impl NaiveDate {
/// ```
#[deprecated(since = "0.4.23", note = "use `and_hms_micro_opt()` instead")]
#[inline]
#[must_use]
pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> NaiveDateTime {
self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
}
@ -835,6 +858,7 @@ impl NaiveDate {
/// assert!(d.and_hms_micro_opt(24, 34, 56, 789_012).is_none());
/// ```
#[inline]
#[must_use]
pub fn and_hms_micro_opt(
&self,
hour: u32,
@ -853,6 +877,7 @@ impl NaiveDate {
/// Panics on invalid hour, minute, second and/or nanosecond.
#[deprecated(since = "0.4.23", note = "use `and_hms_nano_opt()` instead")]
#[inline]
#[must_use]
pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> NaiveDateTime {
self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
}
@ -878,6 +903,7 @@ impl NaiveDate {
/// assert!(d.and_hms_nano_opt(24, 34, 56, 789_012_345).is_none());
/// ```
#[inline]
#[must_use]
pub fn and_hms_nano_opt(
&self,
hour: u32,
@ -926,6 +952,7 @@ impl NaiveDate {
/// Panics when `self` is the last representable date.
#[deprecated(since = "0.4.23", note = "use `succ_opt()` instead")]
#[inline]
#[must_use]
pub fn succ(&self) -> NaiveDate {
self.succ_opt().expect("out of bound")
}
@ -944,6 +971,7 @@ impl NaiveDate {
/// assert_eq!(NaiveDate::MAX.succ_opt(), None);
/// ```
#[inline]
#[must_use]
pub fn succ_opt(&self) -> Option<NaiveDate> {
self.with_of(self.of().succ()).or_else(|| NaiveDate::from_ymd_opt(self.year() + 1, 1, 1))
}
@ -953,6 +981,7 @@ impl NaiveDate {
/// Panics when `self` is the first representable date.
#[deprecated(since = "0.4.23", note = "use `pred_opt()` instead")]
#[inline]
#[must_use]
pub fn pred(&self) -> NaiveDate {
self.pred_opt().expect("out of bound")
}
@ -971,6 +1000,7 @@ impl NaiveDate {
/// assert_eq!(NaiveDate::MIN.pred_opt(), None);
/// ```
#[inline]
#[must_use]
pub fn pred_opt(&self) -> Option<NaiveDate> {
self.with_of(self.of().pred()).or_else(|| NaiveDate::from_ymd_opt(self.year() - 1, 12, 31))
}
@ -993,6 +1023,7 @@ impl NaiveDate {
/// assert_eq!(d.checked_add_signed(TimeDelta::days(-1_000_000_000)), None);
/// assert_eq!(NaiveDate::MAX.checked_add_signed(TimeDelta::days(1)), None);
/// ```
#[must_use]
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDate> {
let year = self.year();
let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400);
@ -1024,6 +1055,7 @@ impl NaiveDate {
/// assert_eq!(d.checked_sub_signed(TimeDelta::days(-1_000_000_000)), None);
/// assert_eq!(NaiveDate::MIN.checked_sub_signed(TimeDelta::days(1)), None);
/// ```
#[must_use]
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDate> {
let year = self.year();
let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400);
@ -1059,6 +1091,7 @@ impl NaiveDate {
/// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(2010, 1, 1)), TimeDelta::days(365*4 + 1));
/// assert_eq!(since(from_ymd(2014, 1, 1), from_ymd(1614, 1, 1)), TimeDelta::days(365*400 + 97));
/// ```
#[must_use]
pub fn signed_duration_since(self, rhs: NaiveDate) -> TimeDelta {
let year1 = self.year();
let year2 = rhs.year();
@ -1072,6 +1105,7 @@ impl NaiveDate {
}
/// Returns the number of whole years from the given `base` until `self`.
#[must_use]
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()) {
@ -1114,6 +1148,7 @@ impl NaiveDate {
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
@ -1157,6 +1192,7 @@ impl NaiveDate {
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}
@ -1165,6 +1201,7 @@ impl NaiveDate {
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[inline]
#[must_use]
pub fn format_localized_with_items<'a, I, B>(
&self,
items: I,
@ -1184,6 +1221,7 @@ impl NaiveDate {
#[cfg(feature = "unstable-locales")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-locales")))]
#[inline]
#[must_use]
pub fn format_localized<'a>(
&self,
fmt: &'a str,
@ -1519,7 +1557,8 @@ impl Datelike for NaiveDate {
/// ```
#[inline]
fn with_month0(&self, month0: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_month(month0 + 1)?)
let month = month0.checked_add(1)?;
self.with_mdf(self.mdf().with_month(month)?)
}
/// Makes a new `NaiveDate` with the day of month (starting from 1) changed.
@ -1557,7 +1596,8 @@ impl Datelike for NaiveDate {
/// ```
#[inline]
fn with_day0(&self, day0: u32) -> Option<NaiveDate> {
self.with_mdf(self.mdf().with_day(day0 + 1)?)
let day = day0.checked_add(1)?;
self.with_mdf(self.mdf().with_day(day)?)
}
/// Makes a new `NaiveDate` with the day of year (starting from 1) changed.
@ -1605,7 +1645,8 @@ impl Datelike for NaiveDate {
/// ```
#[inline]
fn with_ordinal0(&self, ordinal0: u32) -> Option<NaiveDate> {
self.with_of(self.of().with_ordinal(ordinal0 + 1)?)
let ordinal = ordinal0.checked_add(1)?;
self.with_of(self.of().with_ordinal(ordinal)?)
}
}
@ -1788,6 +1829,12 @@ impl Sub<NaiveDate> for NaiveDate {
}
}
impl From<NaiveDateTime> for NaiveDate {
fn from(naive_datetime: NaiveDateTime) -> Self {
naive_datetime.date()
}
}
/// Iterator over `NaiveDate` with a step size of one day.
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)]
pub struct NaiveDateDaysIterator {
@ -2170,7 +2217,7 @@ mod tests {
assert_eq!(
NaiveDate::from_ymd_opt(2022, 8, 3)
.unwrap()
.checked_sub_months(Months::new((i32::MIN as i64).abs() as u32 + 1)),
.checked_sub_months(Months::new(i32::MIN.unsigned_abs() + 1)),
None
);
@ -2421,6 +2468,9 @@ mod tests {
assert_eq!(from_ndays_from_ce(NaiveDate::MIN.num_days_from_ce() - 1), None);
assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce()), Some(NaiveDate::MAX));
assert_eq!(from_ndays_from_ce(NaiveDate::MAX.num_days_from_ce() + 1), None);
assert_eq!(from_ndays_from_ce(i32::MIN), None);
assert_eq!(from_ndays_from_ce(i32::MAX), None);
}
#[test]
@ -2984,4 +3034,12 @@ mod tests {
}
}
}
#[test]
fn test_with_0_overflow() {
let dt = NaiveDate::from_ymd_opt(2023, 4, 18).unwrap();
assert!(dt.with_month0(4294967295).is_none());
assert!(dt.with_day0(4294967295).is_none());
assert!(dt.with_ordinal0(4294967295).is_none());
}
}

View File

@ -145,6 +145,7 @@ impl NaiveDateTime {
/// Panics on the out-of-range number of seconds and/or invalid nanosecond.
#[deprecated(since = "0.4.23", note = "use `from_timestamp_opt()` instead")]
#[inline]
#[must_use]
pub fn from_timestamp(secs: i64, nsecs: u32) -> NaiveDateTime {
let datetime = NaiveDateTime::from_timestamp_opt(secs, nsecs);
datetime.expect("invalid or out-of-range datetime")
@ -172,6 +173,7 @@ impl NaiveDateTime {
/// assert_eq!(timestamp_millis, naive_datetime.unwrap().timestamp_millis());
/// ```
#[inline]
#[must_use]
pub fn from_timestamp_millis(millis: i64) -> Option<NaiveDateTime> {
Self::from_timestamp_unit(millis, TimestampUnit::Millis)
}
@ -198,6 +200,7 @@ impl NaiveDateTime {
/// assert_eq!(timestamp_micros, naive_datetime.unwrap().timestamp_micros());
/// ```
#[inline]
#[must_use]
pub fn from_timestamp_micros(micros: i64) -> Option<NaiveDateTime> {
Self::from_timestamp_unit(micros, TimestampUnit::Micros)
}
@ -229,6 +232,7 @@ impl NaiveDateTime {
/// assert!(from_timestamp_opt(i64::MAX, 0).is_none());
/// ```
#[inline]
#[must_use]
pub fn from_timestamp_opt(secs: i64, nsecs: u32) -> Option<NaiveDateTime> {
let (days, secs) = div_mod_floor(secs, 86_400);
let date = i32::try_from(days)
@ -312,7 +316,7 @@ impl NaiveDateTime {
/// let fmt = "%Y-%m-%d %H:%M:%S";
/// assert!(parse_from_str("10000-09-09 01:46:39", fmt).is_err());
/// assert!(parse_from_str("+10000-09-09 01:46:39", fmt).is_ok());
///```
///```
pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime> {
let mut parsed = Parsed::new();
parse(&mut parsed, s, StrftimeItems::new(fmt))?;
@ -372,6 +376,7 @@ impl NaiveDateTime {
/// assert_eq!(dt.timestamp(), -62198755200);
/// ```
#[inline]
#[must_use]
pub fn timestamp(&self) -> i64 {
const UNIX_EPOCH_DAY: i64 = 719_163;
let gregorian_day = i64::from(self.date.num_days_from_ce());
@ -404,6 +409,7 @@ impl NaiveDateTime {
/// assert_eq!(dt.timestamp_millis(), -900);
/// ```
#[inline]
#[must_use]
pub fn timestamp_millis(&self) -> i64 {
let as_ms = self.timestamp() * 1000;
as_ms + i64::from(self.timestamp_subsec_millis())
@ -431,6 +437,7 @@ impl NaiveDateTime {
/// assert_eq!(dt.timestamp_micros(), 1_000_000_000_000_555);
/// ```
#[inline]
#[must_use]
pub fn timestamp_micros(&self) -> i64 {
let as_us = self.timestamp() * 1_000_000;
as_us + i64::from(self.timestamp_subsec_micros())
@ -470,6 +477,7 @@ impl NaiveDateTime {
/// );
/// ```
#[inline]
#[must_use]
pub fn timestamp_nanos(&self) -> i64 {
let as_ns = self.timestamp() * 1_000_000_000;
as_ns + i64::from(self.timestamp_subsec_nanos())
@ -492,6 +500,7 @@ impl NaiveDateTime {
/// assert_eq!(dt.timestamp_subsec_millis(), 1_234);
/// ```
#[inline]
#[must_use]
pub fn timestamp_subsec_millis(&self) -> u32 {
self.timestamp_subsec_nanos() / 1_000_000
}
@ -513,6 +522,7 @@ impl NaiveDateTime {
/// assert_eq!(dt.timestamp_subsec_micros(), 1_234_567);
/// ```
#[inline]
#[must_use]
pub fn timestamp_subsec_micros(&self) -> u32 {
self.timestamp_subsec_nanos() / 1_000
}
@ -534,6 +544,7 @@ impl NaiveDateTime {
/// assert_eq!(dt.timestamp_subsec_nanos(), 1_234_567_890);
/// ```
#[inline]
#[must_use]
pub fn timestamp_subsec_nanos(&self) -> u32 {
self.time.nanosecond()
}
@ -603,6 +614,7 @@ impl NaiveDateTime {
/// assert_eq!(leap.checked_add_signed(TimeDelta::days(1)),
/// Some(from_ymd(2016, 7, 9).and_hms_milli_opt(3, 5, 59, 300).unwrap()));
/// ```
#[must_use]
pub fn checked_add_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
let (time, rhs) = self.time.overflowing_add_signed(rhs);
@ -639,6 +651,7 @@ impl NaiveDateTime {
/// None
/// );
/// ```
#[must_use]
pub fn checked_add_months(self, rhs: Months) -> Option<NaiveDateTime> {
Some(Self { date: self.date.checked_add_months(rhs)?, time: self.time })
}
@ -704,6 +717,7 @@ impl NaiveDateTime {
/// assert_eq!(leap.checked_sub_signed(TimeDelta::days(1)),
/// Some(from_ymd(2016, 7, 7).and_hms_milli_opt(3, 6, 0, 300).unwrap()));
/// ```
#[must_use]
pub fn checked_sub_signed(self, rhs: TimeDelta) -> Option<NaiveDateTime> {
let (time, rhs) = self.time.overflowing_sub_signed(rhs);
@ -740,6 +754,7 @@ impl NaiveDateTime {
/// None
/// );
/// ```
#[must_use]
pub fn checked_sub_months(self, rhs: Months) -> Option<NaiveDateTime> {
Some(Self { date: self.date.checked_sub_months(rhs)?, time: self.time })
}
@ -747,6 +762,7 @@ impl NaiveDateTime {
/// Add a duration in [`Days`] to the date part of the `NaiveDateTime`
///
/// Returns `None` if the resulting date would be out of range.
#[must_use]
pub fn checked_add_days(self, days: Days) -> Option<Self> {
Some(Self { date: self.date.checked_add_days(days)?, ..self })
}
@ -754,6 +770,7 @@ impl NaiveDateTime {
/// Subtract a duration in [`Days`] from the date part of the `NaiveDateTime`
///
/// Returns `None` if the resulting date would be out of range.
#[must_use]
pub fn checked_sub_days(self, days: Days) -> Option<Self> {
Some(Self { date: self.date.checked_sub_days(days)?, ..self })
}
@ -796,6 +813,7 @@ impl NaiveDateTime {
/// assert_eq!(from_ymd(2015, 7, 1).and_hms_opt(1, 0, 0).unwrap().signed_duration_since(leap),
/// TimeDelta::seconds(3600) - TimeDelta::milliseconds(500));
/// ```
#[must_use]
pub fn signed_duration_since(self, rhs: NaiveDateTime) -> TimeDelta {
self.date.signed_duration_since(rhs.date) + self.time.signed_duration_since(rhs.time)
}
@ -830,6 +848,7 @@ impl NaiveDateTime {
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
@ -873,6 +892,7 @@ impl NaiveDateTime {
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}
@ -894,6 +914,7 @@ impl NaiveDateTime {
/// use chrono::{NaiveDate, Utc};
/// let dt = NaiveDate::from_ymd_opt(2015, 9, 5).unwrap().and_hms_opt(23, 56, 4).unwrap().and_local_timezone(Utc).unwrap();
/// assert_eq!(dt.timezone(), Utc);
#[must_use]
pub fn and_local_timezone<Tz: TimeZone>(&self, tz: Tz) -> LocalResult<DateTime<Tz>> {
tz.from_local_datetime(self)
}

View File

@ -110,6 +110,7 @@ pub mod ts_nanoseconds {
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -136,6 +137,7 @@ pub mod ts_nanoseconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
@ -230,6 +232,7 @@ pub mod ts_nanoseconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699918355733}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -259,6 +262,7 @@ pub mod ts_nanoseconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355733 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
where
D: de::Deserializer<'de>,
@ -356,6 +360,7 @@ pub mod ts_microseconds {
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -382,6 +387,7 @@ pub mod ts_microseconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
@ -479,6 +485,7 @@ pub mod ts_microseconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699918355}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -508,6 +515,7 @@ pub mod ts_microseconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
where
D: de::Deserializer<'de>,
@ -605,6 +613,7 @@ pub mod ts_milliseconds {
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -631,6 +640,7 @@ pub mod ts_milliseconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
@ -725,6 +735,7 @@ pub mod ts_milliseconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699918}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -754,6 +765,7 @@ pub mod ts_milliseconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1526522699918355 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
where
D: de::Deserializer<'de>,
@ -851,6 +863,7 @@ pub mod ts_seconds {
/// assert_eq!(as_string, r#"{"time":1431684000}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(dt: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -877,6 +890,7 @@ pub mod ts_seconds {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
@ -968,6 +982,7 @@ pub mod ts_seconds_option {
/// assert_eq!(as_string, r#"{"time":1526522699}"#);
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn serialize<S>(opt: &Option<NaiveDateTime>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
@ -997,6 +1012,7 @@ pub mod ts_seconds_option {
/// let my_s: S = serde_json::from_str(r#"{ "time": 1431684000 }"#)?;
/// # Ok::<(), serde_json::Error>(())
/// ```
#[must_use]
pub fn deserialize<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
where
D: de::Deserializer<'de>,

View File

@ -115,6 +115,7 @@ impl YearFlags {
#[allow(unreachable_pub)] // public as an alias for benchmarks only
#[doc(hidden)] // for benchmarks only
#[inline]
#[must_use]
pub fn from_year(year: i32) -> YearFlags {
let year = mod_floor(year, 400);
YearFlags::from_year_mod_400(year)

View File

@ -210,6 +210,7 @@ impl NaiveTime {
/// Panics on invalid hour, minute and/or second.
#[deprecated(since = "0.4.23", note = "use `from_hms_opt()` instead")]
#[inline]
#[must_use]
pub fn from_hms(hour: u32, min: u32, sec: u32) -> NaiveTime {
NaiveTime::from_hms_opt(hour, min, sec).expect("invalid time")
}
@ -235,6 +236,7 @@ impl NaiveTime {
/// assert!(from_hms_opt(23, 59, 60).is_none());
/// ```
#[inline]
#[must_use]
pub const fn from_hms_opt(hour: u32, min: u32, sec: u32) -> Option<NaiveTime> {
NaiveTime::from_hms_nano_opt(hour, min, sec, 0)
}
@ -247,6 +249,7 @@ impl NaiveTime {
/// Panics on invalid hour, minute, second and/or millisecond.
#[deprecated(since = "0.4.23", note = "use `from_hms_milli_opt()` instead")]
#[inline]
#[must_use]
pub fn from_hms_milli(hour: u32, min: u32, sec: u32, milli: u32) -> NaiveTime {
NaiveTime::from_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
}
@ -274,6 +277,7 @@ impl NaiveTime {
/// assert!(from_hmsm_opt(23, 59, 59, 2_000).is_none());
/// ```
#[inline]
#[must_use]
pub fn from_hms_milli_opt(hour: u32, min: u32, sec: u32, milli: u32) -> Option<NaiveTime> {
milli
.checked_mul(1_000_000)
@ -288,6 +292,7 @@ impl NaiveTime {
/// Panics on invalid hour, minute, second and/or microsecond.
#[deprecated(since = "0.4.23", note = "use `from_hms_micro_opt()` instead")]
#[inline]
#[must_use]
pub fn from_hms_micro(hour: u32, min: u32, sec: u32, micro: u32) -> NaiveTime {
NaiveTime::from_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
}
@ -315,6 +320,7 @@ impl NaiveTime {
/// assert!(from_hmsu_opt(23, 59, 59, 2_000_000).is_none());
/// ```
#[inline]
#[must_use]
pub fn from_hms_micro_opt(hour: u32, min: u32, sec: u32, micro: u32) -> Option<NaiveTime> {
micro.checked_mul(1_000).and_then(|nano| NaiveTime::from_hms_nano_opt(hour, min, sec, nano))
}
@ -327,6 +333,7 @@ impl NaiveTime {
/// Panics on invalid hour, minute, second and/or nanosecond.
#[deprecated(since = "0.4.23", note = "use `from_hms_nano_opt()` instead")]
#[inline]
#[must_use]
pub fn from_hms_nano(hour: u32, min: u32, sec: u32, nano: u32) -> NaiveTime {
NaiveTime::from_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
}
@ -354,6 +361,7 @@ impl NaiveTime {
/// assert!(from_hmsn_opt(23, 59, 59, 2_000_000_000).is_none());
/// ```
#[inline]
#[must_use]
pub const fn from_hms_nano_opt(hour: u32, min: u32, sec: u32, nano: u32) -> Option<NaiveTime> {
if hour >= 24 || min >= 60 || sec >= 60 || nano >= 2_000_000_000 {
return None;
@ -370,6 +378,7 @@ impl NaiveTime {
/// Panics on invalid number of seconds and/or nanosecond.
#[deprecated(since = "0.4.23", note = "use `from_num_seconds_from_midnight_opt()` instead")]
#[inline]
#[must_use]
pub fn from_num_seconds_from_midnight(secs: u32, nano: u32) -> NaiveTime {
NaiveTime::from_num_seconds_from_midnight_opt(secs, nano).expect("invalid time")
}
@ -395,6 +404,7 @@ impl NaiveTime {
/// assert!(from_nsecs_opt(86399, 2_000_000_000).is_none());
/// ```
#[inline]
#[must_use]
pub const fn from_num_seconds_from_midnight_opt(secs: u32, nano: u32) -> Option<NaiveTime> {
if secs >= 86_400 || nano >= 2_000_000_000 {
return None;
@ -488,6 +498,7 @@ impl NaiveTime {
/// assert_eq!(from_hms(3, 4, 5).overflowing_add_signed(TimeDelta::hours(-7)),
/// (from_hms(20, 4, 5), -86_400));
/// ```
#[must_use]
pub fn overflowing_add_signed(&self, mut rhs: TimeDelta) -> (NaiveTime, i64) {
let mut secs = self.secs;
let mut frac = self.frac;
@ -571,6 +582,7 @@ impl NaiveTime {
/// (from_hms(1, 4, 5), -86_400));
/// ```
#[inline]
#[must_use]
pub fn overflowing_sub_signed(&self, rhs: TimeDelta) -> (NaiveTime, i64) {
let (time, rhs) = self.overflowing_add_signed(-rhs);
(time, -rhs) // safe to negate, rhs is within +/- (2^63 / 1000)
@ -630,6 +642,7 @@ impl NaiveTime {
/// assert_eq!(since(from_hmsm(3, 0, 59, 1_000), from_hmsm(2, 59, 59, 1_000)),
/// TimeDelta::seconds(61));
/// ```
#[must_use]
pub fn signed_duration_since(self, rhs: NaiveTime) -> TimeDelta {
// | | :leap| | | | | | | :leap| |
// | | : | | | | | | | : | |
@ -692,6 +705,7 @@ impl NaiveTime {
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format_with_items<'a, I, B>(&self, items: I) -> DelayedFormat<I>
where
I: Iterator<Item = B> + Clone,
@ -737,6 +751,7 @@ impl NaiveTime {
#[cfg(any(feature = "alloc", feature = "std", test))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[inline]
#[must_use]
pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> {
self.format_with_items(StrftimeItems::new(fmt))
}

View File

@ -34,6 +34,7 @@ impl FixedOffset {
///
/// Panics on the out-of-bound `secs`.
#[deprecated(since = "0.4.23", note = "use `east_opt()` instead")]
#[must_use]
pub fn east(secs: i32) -> FixedOffset {
FixedOffset::east_opt(secs).expect("FixedOffset::east out of bounds")
}
@ -52,6 +53,7 @@ impl FixedOffset {
/// .and_hms_opt(0, 0, 0).unwrap();
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00+05:00")
/// ```
#[must_use]
pub const fn east_opt(secs: i32) -> Option<FixedOffset> {
if -86_400 < secs && secs < 86_400 {
Some(FixedOffset { local_minus_utc: secs })
@ -65,6 +67,7 @@ impl FixedOffset {
///
/// Panics on the out-of-bound `secs`.
#[deprecated(since = "0.4.23", note = "use `west_opt()` instead")]
#[must_use]
pub fn west(secs: i32) -> FixedOffset {
FixedOffset::west_opt(secs).expect("FixedOffset::west out of bounds")
}
@ -83,6 +86,7 @@ impl FixedOffset {
/// .and_hms_opt(0, 0, 0).unwrap();
/// assert_eq!(&datetime.to_rfc3339(), "2016-11-08T00:00:00-05:00")
/// ```
#[must_use]
pub const fn west_opt(secs: i32) -> Option<FixedOffset> {
if -86_400 < secs && secs < 86_400 {
Some(FixedOffset { local_minus_utc: -secs })

View File

@ -60,6 +60,7 @@ impl Local {
/// Returns a `Date` which corresponds to the current date.
#[deprecated(since = "0.4.23", note = "use `Local::now()` instead")]
#[allow(deprecated)]
#[must_use]
pub fn today() -> Date<Local> {
Local::now().date()
}
@ -70,6 +71,7 @@ impl Local {
feature = "wasmbind",
not(any(target_os = "emscripten", target_os = "wasi"))
)))]
#[must_use]
pub fn now() -> DateTime<Local> {
inner::now()
}
@ -80,6 +82,7 @@ impl Local {
feature = "wasmbind",
not(any(target_os = "emscripten", target_os = "wasi"))
))]
#[must_use]
pub fn now() -> DateTime<Local> {
use super::Utc;
let now: DateTime<Utc> = super::Utc::now();

View File

@ -100,21 +100,6 @@ impl From<Utf8Error> for Error {
}
}
// MSRV: 1.38
#[inline]
const fn rem_euclid(v: i64, rhs: i64) -> i64 {
let r = v % rhs;
if r < 0 {
if rhs < 0 {
r - rhs
} else {
r + rhs
}
} else {
r
}
}
/// Number of hours in one day
const HOURS_PER_DAY: i64 = 24;
/// Number of seconds in one hour

View File

@ -7,7 +7,6 @@ use super::rule::TransitionRule;
use super::timezone::{LeapSecond, LocalTimeType, TimeZone, Transition};
use super::Error;
#[allow(clippy::map_clone)] // MSRV: 1.36
pub(super) fn parse(bytes: &[u8]) -> Result<TimeZone, Error> {
let mut cursor = Cursor::new(bytes);
let state = State::new(&mut cursor, true)?;
@ -66,8 +65,8 @@ pub(super) fn parse(bytes: &[u8]) -> Result<TimeZone, Error> {
leap_seconds.push(LeapSecond::new(unix_leap_time, correction));
}
let std_walls_iter = state.std_walls.iter().map(|&i| i).chain(iter::repeat(0));
let ut_locals_iter = state.ut_locals.iter().map(|&i| i).chain(iter::repeat(0));
let std_walls_iter = state.std_walls.iter().copied().chain(iter::repeat(0));
let ut_locals_iter = state.ut_locals.iter().copied().chain(iter::repeat(0));
if std_walls_iter.zip(ut_locals_iter).take(state.header.type_count).any(|pair| pair == (0, 1)) {
return Err(Error::InvalidTzFile(
"invalid couple of standard/wall and UT/local indicators",

View File

@ -3,7 +3,7 @@ use std::cmp::Ordering;
use super::parser::Cursor;
use super::timezone::{LocalTimeType, SECONDS_PER_WEEK};
use super::{
rem_euclid, Error, CUMUL_DAY_IN_MONTHS_NORMAL_YEAR, DAYS_PER_WEEK, DAY_IN_MONTHS_NORMAL_YEAR,
Error, CUMUL_DAY_IN_MONTHS_NORMAL_YEAR, DAYS_PER_WEEK, DAY_IN_MONTHS_NORMAL_YEAR,
SECONDS_PER_DAY,
};
@ -589,9 +589,9 @@ impl RuleDay {
}
let week_day_of_first_month_day =
rem_euclid(4 + days_since_unix_epoch(year, month, 1), DAYS_PER_WEEK);
(4 + days_since_unix_epoch(year, month, 1)).rem_euclid(DAYS_PER_WEEK);
let first_week_day_occurence_in_month =
1 + rem_euclid(week_day as i64 - week_day_of_first_month_day, DAYS_PER_WEEK);
1 + (week_day as i64 - week_day_of_first_month_day).rem_euclid(DAYS_PER_WEEK);
let mut month_day =
first_week_day_occurence_in_month + (week as i64 - 1) * DAYS_PER_WEEK;
@ -777,7 +777,6 @@ mod tests {
use super::super::timezone::Transition;
use super::super::{Error, TimeZone};
use super::{AlternateTime, LocalTimeType, RuleDay, TransitionRule};
use crate::matches;
#[test]
fn test_quoted() -> Result<(), Error> {

View File

@ -620,8 +620,8 @@ const fn saturating_abs(v: i32) -> i32 {
// Possible system timezone directories
#[cfg(unix)]
const ZONE_INFO_DIRECTORIES: [&str; 3] =
["/usr/share/zoneinfo", "/share/zoneinfo", "/etc/zoneinfo"];
const ZONE_INFO_DIRECTORIES: [&str; 4] =
["/usr/share/zoneinfo", "/share/zoneinfo", "/etc/zoneinfo", "/usr/share/lib/zoneinfo"];
/// Number of seconds in one week
pub(crate) const SECONDS_PER_WEEK: i64 = SECONDS_PER_DAY * DAYS_PER_WEEK;
@ -632,7 +632,6 @@ const SECONDS_PER_28_DAYS: i64 = SECONDS_PER_DAY * 28;
mod tests {
use super::super::Error;
use super::{LeapSecond, LocalTimeType, TimeZone, TimeZoneName, Transition, TransitionRule};
use crate::matches;
#[test]
fn test_no_dst() -> Result<(), Error> {

View File

@ -8,15 +8,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::io;
use std::mem;
use core::mem::MaybeUninit;
use std::io::Error;
use std::ptr;
use std::time::{SystemTime, UNIX_EPOCH};
use std::result::Result;
use windows_sys::Win32::Foundation::FILETIME;
use windows_sys::Win32::Foundation::SYSTEMTIME;
use windows_sys::Win32::System::Time::FileTimeToSystemTime;
use windows_sys::Win32::System::Time::GetTimeZoneInformation;
use windows_sys::Win32::System::SystemInformation::GetLocalTime;
use windows_sys::Win32::System::Time::SystemTimeToFileTime;
use windows_sys::Win32::System::Time::SystemTimeToTzSpecificLocalTime;
use windows_sys::Win32::System::Time::TzSpecificLocalTimeToSystemTime;
@ -24,268 +23,143 @@ use windows_sys::Win32::System::Time::TzSpecificLocalTimeToSystemTime;
use super::{FixedOffset, Local};
use crate::{DateTime, Datelike, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, Timelike};
pub(super) fn now() -> DateTime<Local> {
let datetime = tm_to_datetime(Timespec::now().local());
datetime.single().expect("invalid time")
}
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
let tm = Tm {
tm_sec: d.second() as i32,
tm_min: d.minute() as i32,
tm_hour: d.hour() as i32,
tm_mday: d.day() as i32,
tm_mon: d.month0() as i32, // yes, C is that strange...
tm_year: d.year() - 1900, // this doesn't underflow, we know that d is `NaiveDateTime`.
tm_wday: 0, // to_local ignores this
tm_yday: 0, // and this
tm_isdst: -1,
// This seems pretty fake?
tm_utcoff: i32::from(local),
// do not set this, OS APIs are heavily inconsistent in terms of leap second handling
tm_nsec: 0,
};
let spec = Timespec {
sec: match local {
false => utc_tm_to_time(&tm),
true => local_tm_to_time(&tm),
},
nsec: tm.tm_nsec,
};
// Adjust for leap seconds
let mut tm = spec.local();
assert_eq!(tm.tm_nsec, 0);
tm.tm_nsec = d.nanosecond() as i32;
tm_to_datetime(tm)
}
/// Converts a `time::Tm` struct into the timezone-aware `DateTime`.
fn tm_to_datetime(mut tm: Tm) -> LocalResult<DateTime<Local>> {
if tm.tm_sec >= 60 {
tm.tm_nsec += (tm.tm_sec - 59) * 1_000_000_000;
tm.tm_sec = 59;
}
let date = NaiveDate::from_ymd_opt(tm.tm_year + 1900, tm.tm_mon as u32 + 1, tm.tm_mday as u32)
.unwrap();
let time = NaiveTime::from_hms_nano_opt(
tm.tm_hour as u32,
tm.tm_min as u32,
tm.tm_sec as u32,
tm.tm_nsec as u32,
);
match time {
Some(time) => {
let offset = FixedOffset::east_opt(tm.tm_utcoff).unwrap();
let datetime = DateTime::from_utc(date.and_time(time) - offset, offset);
// #TODO - there should be ambiguous cases, investigate?
LocalResult::Single(datetime)
}
None => LocalResult::None,
}
}
/// A record specifying a time value in seconds and nanoseconds, where
/// nanoseconds represent the offset from the given second.
/// This macro calls a Windows API FFI and checks whether the function errored with the provided error_id. If an error returns,
/// the macro will return an `Error::last_os_error()`.
///
/// For example a timespec of 1.2 seconds after the beginning of the epoch would
/// be represented as {sec: 1, nsec: 200000000}.
struct Timespec {
sec: i64,
nsec: i32,
}
impl Timespec {
/// Constructs a timespec representing the current time in UTC.
fn now() -> Timespec {
let st =
SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch");
Timespec { sec: st.as_secs() as i64, nsec: st.subsec_nanos() as i32 }
/// # Safety
///
/// The provided error ID must align with the provided Windows API, providing the wrong ID could lead to UB.
macro_rules! windows_sys_call {
($name:ident($($arg:expr),*), $error_id:expr) => {
if $name($($arg),*) == $error_id {
return Err(Error::last_os_error());
}
}
/// Converts this timespec into the system's local time.
fn local(self) -> Tm {
let mut tm = Tm {
tm_sec: 0,
tm_min: 0,
tm_hour: 0,
tm_mday: 0,
tm_mon: 0,
tm_year: 0,
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
tm_utcoff: 0,
tm_nsec: 0,
};
time_to_local_tm(self.sec, &mut tm);
tm.tm_nsec = self.nsec;
tm
}
}
/// Holds a calendar date and time broken down into its components (year, month,
/// day, and so on), also called a broken-down time value.
// FIXME: use c_int instead of i32?
#[repr(C)]
struct Tm {
/// Seconds after the minute - [0, 60]
tm_sec: i32,
/// Minutes after the hour - [0, 59]
tm_min: i32,
/// Hours after midnight - [0, 23]
tm_hour: i32,
/// Day of the month - [1, 31]
tm_mday: i32,
/// Months since January - [0, 11]
tm_mon: i32,
/// Years since 1900
tm_year: i32,
/// Days since Sunday - [0, 6]. 0 = Sunday, 1 = Monday, ..., 6 = Saturday.
tm_wday: i32,
/// Days since January 1 - [0, 365]
tm_yday: i32,
/// Daylight Saving Time flag.
///
/// This value is positive if Daylight Saving Time is in effect, zero if
/// Daylight Saving Time is not in effect, and negative if this information
/// is not available.
tm_isdst: i32,
/// Identifies the time zone that was used to compute this broken-down time
/// value, including any adjustment for Daylight Saving Time. This is the
/// number of seconds east of UTC. For example, for U.S. Pacific Daylight
/// Time, the value is `-7*60*60 = -25200`.
tm_utcoff: i32,
/// Nanoseconds after the second - [0, 10<sup>9</sup> - 1]
tm_nsec: i32,
}
const HECTONANOSECS_IN_SEC: i64 = 10_000_000;
const HECTONANOSEC_TO_UNIX_EPOCH: i64 = 11_644_473_600 * HECTONANOSECS_IN_SEC;
fn time_to_file_time(sec: i64) -> FILETIME {
let t = ((sec * HECTONANOSECS_IN_SEC) + HECTONANOSEC_TO_UNIX_EPOCH) as u64;
FILETIME { dwLowDateTime: t as u32, dwHighDateTime: (t >> 32) as u32 }
pub(super) fn now() -> DateTime<Local> {
LocalSysTime::local().datetime()
}
fn file_time_as_u64(ft: &FILETIME) -> u64 {
((ft.dwHighDateTime as u64) << 32) | (ft.dwLowDateTime as u64)
/// Converts a local `NaiveDateTime` to the `time::Timespec`.
pub(super) fn naive_to_local(d: &NaiveDateTime, local: bool) -> LocalResult<DateTime<Local>> {
let naive_sys_time = system_time_from_naive_date_time(d);
let local_sys_time = match local {
false => LocalSysTime::from_utc_time(naive_sys_time),
true => LocalSysTime::from_local_time(naive_sys_time),
};
if let Ok(local) = local_sys_time {
return LocalResult::Single(local.datetime());
}
LocalResult::None
}
fn file_time_to_unix_seconds(ft: &FILETIME) -> i64 {
let t = file_time_as_u64(ft) as i64;
((t - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC) as i64
struct LocalSysTime {
inner: SYSTEMTIME,
offset: i32,
}
fn system_time_to_file_time(sys: &SYSTEMTIME) -> FILETIME {
unsafe {
let mut ft = mem::zeroed();
SystemTimeToFileTime(sys, &mut ft);
ft
impl LocalSysTime {
fn local() -> Self {
let mut now = MaybeUninit::<SYSTEMTIME>::uninit();
unsafe { GetLocalTime(now.as_mut_ptr()) }
// SAFETY: GetLocalTime cannot fail according to spec, so we can assume the value
// is initialized.
let st = unsafe { now.assume_init() };
Self::from_local_time(st).expect("Current local time must exist")
}
fn from_utc_time(utc_time: SYSTEMTIME) -> Result<Self, Error> {
let local_time = utc_to_local_time(&utc_time)?;
let utc_secs = system_time_as_unix_seconds(&utc_time)?;
let local_secs = system_time_as_unix_seconds(&local_time)?;
let offset = (local_secs - utc_secs) as i32;
Ok(Self { inner: local_time, offset })
}
fn from_local_time(local_time: SYSTEMTIME) -> Result<Self, Error> {
let utc_time = local_to_utc_time(&local_time)?;
let utc_secs = system_time_as_unix_seconds(&utc_time)?;
let local_secs = system_time_as_unix_seconds(&local_time)?;
let offset = (local_secs - utc_secs) as i32;
Ok(Self { inner: local_time, offset })
}
fn datetime(self) -> DateTime<Local> {
let st = self.inner;
let date =
NaiveDate::from_ymd_opt(st.wYear as i32, st.wMonth as u32, st.wDay as u32).unwrap();
let time = NaiveTime::from_hms(st.wHour as u32, st.wMinute as u32, st.wSecond as u32);
let offset = FixedOffset::east_opt(self.offset).unwrap();
DateTime::from_utc(date.and_time(time) - offset, offset)
}
}
fn tm_to_system_time(tm: &Tm) -> SYSTEMTIME {
let mut sys: SYSTEMTIME = unsafe { mem::zeroed() };
sys.wSecond = tm.tm_sec as u16;
sys.wMinute = tm.tm_min as u16;
sys.wHour = tm.tm_hour as u16;
sys.wDay = tm.tm_mday as u16;
sys.wDayOfWeek = tm.tm_wday as u16;
sys.wMonth = (tm.tm_mon + 1) as u16;
sys.wYear = (tm.tm_year + 1900) as u16;
sys
fn system_time_from_naive_date_time(dt: &NaiveDateTime) -> SYSTEMTIME {
SYSTEMTIME {
// Valid values: 1601-30827
wYear: dt.year() as u16,
// Valid values:1-12
wMonth: dt.month() as u16,
// Valid values: 0-6, starting Sunday.
// NOTE: enum returns 1-7, starting Monday, so we are
// off here, but this is not currently used in local.
wDayOfWeek: dt.weekday() as u16,
// Valid values: 1-31
wDay: dt.day() as u16,
// Valid values: 0-23
wHour: dt.hour() as u16,
// Valid values: 0-59
wMinute: dt.minute() as u16,
// Valid values: 0-59
wSecond: dt.second() as u16,
// Valid values: 0-999
wMilliseconds: 0,
}
}
fn system_time_to_tm(sys: &SYSTEMTIME, tm: &mut Tm) {
tm.tm_sec = sys.wSecond as i32;
tm.tm_min = sys.wMinute as i32;
tm.tm_hour = sys.wHour as i32;
tm.tm_mday = sys.wDay as i32;
tm.tm_wday = sys.wDayOfWeek as i32;
tm.tm_mon = (sys.wMonth - 1) as i32;
tm.tm_year = (sys.wYear - 1900) as i32;
tm.tm_yday = yday(tm.tm_year, tm.tm_mon + 1, tm.tm_mday);
fn yday(year: i32, month: i32, day: i32) -> i32 {
let leap = if month > 2 {
if year % 4 == 0 {
1
} else {
2
}
} else {
pub(crate) fn local_to_utc_time(local: &SYSTEMTIME) -> Result<SYSTEMTIME, Error> {
let mut sys_time = MaybeUninit::<SYSTEMTIME>::uninit();
unsafe {
windows_sys_call!(
TzSpecificLocalTimeToSystemTime(ptr::null(), local, sys_time.as_mut_ptr()),
0
};
let july = i32::from(month > 7);
(month - 1) * 30 + month / 2 + (day - 1) - leap + july
}
)
};
// SAFETY: TzSpecificLocalTimeToSystemTime must have succeeded at this point, so we can
// assume the value is initialized.
Ok(unsafe { sys_time.assume_init() })
}
macro_rules! call {
($name:ident($($arg:expr),*)) => {
if $name($($arg),*) == 0 {
panic!(concat!(stringify!($name), " failed with: {}"),
io::Error::last_os_error());
}
}
}
fn time_to_local_tm(sec: i64, tm: &mut Tm) {
let ft = time_to_file_time(sec);
pub(crate) fn utc_to_local_time(utc_time: &SYSTEMTIME) -> Result<SYSTEMTIME, Error> {
let mut local = MaybeUninit::<SYSTEMTIME>::uninit();
unsafe {
let mut utc = mem::zeroed();
let mut local = mem::zeroed();
call!(FileTimeToSystemTime(&ft, &mut utc));
call!(SystemTimeToTzSpecificLocalTime(ptr::null(), &utc, &mut local));
system_time_to_tm(&local, tm);
let local = system_time_to_file_time(&local);
let local_sec = file_time_to_unix_seconds(&local);
let mut tz = mem::zeroed();
GetTimeZoneInformation(&mut tz);
// SystemTimeToTzSpecificLocalTime already applied the biases so
// check if it non standard
tm.tm_utcoff = (local_sec - sec) as i32;
tm.tm_isdst = if tm.tm_utcoff == -60 * (tz.Bias + tz.StandardBias) { 0 } else { 1 };
}
windows_sys_call!(
SystemTimeToTzSpecificLocalTime(ptr::null(), utc_time, local.as_mut_ptr()),
0
)
};
// SAFETY: SystemTimeToTzSpecificLocalTime must have succeeded at this point, so we can
// assume the value is initialized.
Ok(unsafe { local.assume_init() })
}
fn utc_tm_to_time(tm: &Tm) -> i64 {
unsafe {
let mut ft = mem::zeroed();
let sys_time = tm_to_system_time(tm);
call!(SystemTimeToFileTime(&sys_time, &mut ft));
file_time_to_unix_seconds(&ft)
}
}
fn local_tm_to_time(tm: &Tm) -> i64 {
unsafe {
let mut ft = mem::zeroed();
let mut utc = mem::zeroed();
let sys_time = tm_to_system_time(tm);
call!(TzSpecificLocalTimeToSystemTime(ptr::null(), &sys_time, &mut utc));
call!(SystemTimeToFileTime(&utc, &mut ft));
file_time_to_unix_seconds(&ft)
}
/// Returns a i64 value representing the unix seconds conversion of the current `WinSystemTime`.
pub(crate) fn system_time_as_unix_seconds(st: &SYSTEMTIME) -> Result<i64, Error> {
let mut init = MaybeUninit::<FILETIME>::uninit();
unsafe { windows_sys_call!(SystemTimeToFileTime(st, init.as_mut_ptr()), 0) }
// SystemTimeToFileTime must have succeeded at this point, so we can assum the value is
// initalized.
let filetime = unsafe { init.assume_init() };
let bit_shift = ((filetime.dwHighDateTime as u64) << 32) | (filetime.dwLowDateTime as u64);
let unix_secs = (bit_shift as i64 - HECTONANOSEC_TO_UNIX_EPOCH) / HECTONANOSECS_IN_SEC;
Ok(unix_secs)
}

View File

@ -52,6 +52,7 @@ pub enum LocalResult<T> {
impl<T> LocalResult<T> {
/// Returns `Some` only when the conversion result is unique, or `None` otherwise.
#[must_use]
pub fn single(self) -> Option<T> {
match self {
LocalResult::Single(t) => Some(t),
@ -60,6 +61,7 @@ impl<T> LocalResult<T> {
}
/// Returns `Some` for the earliest possible conversion result, or `None` if none.
#[must_use]
pub fn earliest(self) -> Option<T> {
match self {
LocalResult::Single(t) | LocalResult::Ambiguous(t, _) => Some(t),
@ -68,6 +70,7 @@ impl<T> LocalResult<T> {
}
/// Returns `Some` for the latest possible conversion result, or `None` if none.
#[must_use]
pub fn latest(self) -> Option<T> {
match self {
LocalResult::Single(t) | LocalResult::Ambiguous(_, t) => Some(t),
@ -76,6 +79,7 @@ impl<T> LocalResult<T> {
}
/// Maps a `LocalResult<T>` into `LocalResult<U>` with given function.
#[must_use]
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> LocalResult<U> {
match self {
LocalResult::None => LocalResult::None,
@ -92,6 +96,7 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
#[must_use]
pub fn and_time(self, time: NaiveTime) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => {
@ -106,6 +111,7 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
#[must_use]
pub fn and_hms_opt(self, hour: u32, min: u32, sec: u32) -> LocalResult<DateTime<Tz>> {
match self {
LocalResult::Single(d) => {
@ -121,6 +127,7 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
#[must_use]
pub fn and_hms_milli_opt(
self,
hour: u32,
@ -142,6 +149,7 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
#[must_use]
pub fn and_hms_micro_opt(
self,
hour: u32,
@ -163,6 +171,7 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
///
/// Propagates any error. Ambiguous result would be discarded.
#[inline]
#[must_use]
pub fn and_hms_nano_opt(
self,
hour: u32,
@ -181,6 +190,8 @@ impl<Tz: TimeZone> LocalResult<Date<Tz>> {
impl<T: fmt::Debug> LocalResult<T> {
/// Returns the single unique conversion result, or panics accordingly.
#[must_use]
#[track_caller]
pub fn unwrap(self) -> T {
match self {
LocalResult::None => panic!("No such local time"),

View File

@ -54,6 +54,7 @@ impl Utc {
note = "use `Utc::now()` instead, potentially with `.date_naive()`"
)]
#[allow(deprecated)]
#[must_use]
pub fn today() -> Date<Utc> {
Utc::now().date()
}
@ -64,6 +65,7 @@ impl Utc {
feature = "wasmbind",
not(any(target_os = "emscripten", target_os = "wasi"))
)))]
#[must_use]
pub fn now() -> DateTime<Utc> {
let now =
SystemTime::now().duration_since(UNIX_EPOCH).expect("system time before Unix epoch");
@ -78,6 +80,7 @@ impl Utc {
feature = "wasmbind",
not(any(target_os = "emscripten", target_os = "wasi"))
))]
#[must_use]
pub fn now() -> DateTime<Utc> {
let now = js_sys::Date::new_0();
DateTime::<Utc>::from(now)

View File

@ -74,6 +74,7 @@ impl TimeDelta {
/// Equivalent to `Duration::seconds(weeks * 7 * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn weeks(weeks: i64) -> TimeDelta {
let secs = weeks.checked_mul(SECS_PER_WEEK).expect("Duration::weeks out of bounds");
TimeDelta::seconds(secs)
@ -83,6 +84,7 @@ impl TimeDelta {
/// Equivalent to `Duration::seconds(days * 24 * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn days(days: i64) -> TimeDelta {
let secs = days.checked_mul(SECS_PER_DAY).expect("Duration::days out of bounds");
TimeDelta::seconds(secs)
@ -92,6 +94,7 @@ impl TimeDelta {
/// Equivalent to `Duration::seconds(hours * 60 * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn hours(hours: i64) -> TimeDelta {
let secs = hours.checked_mul(SECS_PER_HOUR).expect("Duration::hours ouf of bounds");
TimeDelta::seconds(secs)
@ -101,6 +104,7 @@ impl TimeDelta {
/// Equivalent to `Duration::seconds(minutes * 60)` with overflow checks.
/// Panics when the duration is out of bounds.
#[inline]
#[must_use]
pub fn minutes(minutes: i64) -> TimeDelta {
let secs = minutes.checked_mul(SECS_PER_MINUTE).expect("Duration::minutes out of bounds");
TimeDelta::seconds(secs)
@ -110,6 +114,7 @@ impl TimeDelta {
/// Panics when the duration is more than `i64::MAX` seconds
/// or less than `i64::MIN` seconds.
#[inline]
#[must_use]
pub fn seconds(seconds: i64) -> TimeDelta {
let d = TimeDelta { secs: seconds, nanos: 0 };
if d < MIN || d > MAX {
@ -211,6 +216,7 @@ impl TimeDelta {
}
/// Add two durations, returning `None` if overflow occurred.
#[must_use]
pub fn checked_add(&self, rhs: &TimeDelta) -> Option<TimeDelta> {
let mut secs = try_opt!(self.secs.checked_add(rhs.secs));
let mut nanos = self.nanos + rhs.nanos;
@ -229,6 +235,7 @@ impl TimeDelta {
}
/// Subtract two durations, returning `None` if overflow occurred.
#[must_use]
pub fn checked_sub(&self, rhs: &TimeDelta) -> Option<TimeDelta> {
let mut secs = try_opt!(self.secs.checked_sub(rhs.secs));
let mut nanos = self.nanos - rhs.nanos;

View File

@ -10,6 +10,26 @@ use crate::OutOfRange;
/// The order of the days of week depends on the context.
/// (This is why this type does *not* implement `PartialOrd` or `Ord` traits.)
/// One should prefer `*_from_monday` or `*_from_sunday` methods to get the correct result.
///
/// # Example
/// ```
/// use chrono::Weekday;
/// use std::convert::TryFrom;
///
/// let monday = "Monday".parse::<Weekday>().unwrap();
/// assert_eq!(monday, Weekday::Mon);
///
/// let sunday = Weekday::try_from(6).unwrap();
/// assert_eq!(sunday, Weekday::Sun);
///
/// assert_eq!(sunday.num_days_from_monday(), 6); // starts counting with Monday = 0
/// assert_eq!(sunday.number_from_monday(), 7); // starts counting with Monday = 1
/// assert_eq!(sunday.num_days_from_sunday(), 0); // starts counting with Sunday = 0
/// assert_eq!(sunday.number_from_sunday(), 1); // starts counting with Sunday = 1
///
/// assert_eq!(sunday.succ(), monday);
/// assert_eq!(sunday.pred(), Weekday::Sat);
/// ```
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
#[cfg_attr(feature = "rkyv", derive(Archive, Deserialize, Serialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
@ -37,6 +57,7 @@ impl Weekday {
/// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
/// `w.succ()`: | `Tue` | `Wed` | `Thu` | `Fri` | `Sat` | `Sun` | `Mon`
#[inline]
#[must_use]
pub const fn succ(&self) -> Weekday {
match *self {
Weekday::Mon => Weekday::Tue,
@ -55,6 +76,7 @@ impl Weekday {
/// ----------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
/// `w.pred()`: | `Sun` | `Mon` | `Tue` | `Wed` | `Thu` | `Fri` | `Sat`
#[inline]
#[must_use]
pub const fn pred(&self) -> Weekday {
match *self {
Weekday::Mon => Weekday::Sun,
@ -109,7 +131,7 @@ impl Weekday {
/// Returns a day-of-week number starting from the parameter `day` (D) = 0.
///
/// `w`: | `D` | `D+1` | `D+2` | `D+3` | `D+4` | `D+5` | `D+6`
/// `w`: | `D` | `D+1` | `D+2` | `D+3` | `D+4` | `D+5` | `D+6`
/// --------------------------- | ----- | ----- | ----- | ----- | ----- | ----- | -----
/// `w.num_days_from(wd)`: | 0 | 1 | 2 | 3 | 4 | 5 | 6
#[inline]

View File

@ -1,7 +1,11 @@
#[cfg(unix)]
use chrono::offset::TimeZone;
#[cfg(unix)]
use chrono::Local;
#[cfg(unix)]
use chrono::{Datelike, NaiveDate, NaiveDateTime, Timelike};
#[cfg(unix)]
use std::{path, process};
#[cfg(unix)]
@ -51,7 +55,10 @@ fn verify_against_date_command_local(path: &'static str, dt: NaiveDateTime) {
#[test]
#[cfg(unix)]
fn try_verify_against_date_command() {
#[cfg(not(target_os = "aix"))]
let date_path = "/usr/bin/date";
#[cfg(target_os = "aix")]
let date_path = "/opt/freeware/bin/date";
if !path::Path::new(date_path).exists() {
// date command not found, skipping