mirror of
https://github.com/chronotope/chrono.git
synced 2025-09-29 22:11:59 +00:00
Merge branch '0.4.x' into main
This commit is contained in:
commit
7926f011be
7
.github/workflows/test.yml
vendored
7
.github/workflows/test.yml
vendored
@ -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
|
||||
|
@ -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"] }
|
||||
|
@ -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));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -1 +0,0 @@
|
||||
msrv = "1.48"
|
27
src/date.rs
27
src/date.rs
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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>,
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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))?;
|
||||
|
@ -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();
|
||||
|
12
src/lib.rs
12
src/lib.rs
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
src/month.rs
15
src/month.rs
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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 })
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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> {
|
||||
|
@ -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> {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user