Merge branch 'main'

This commit is contained in:
Paul Dicker 2024-03-11 12:03:52 +01:00
commit bc95157246
11 changed files with 137 additions and 113 deletions

View File

@ -29,10 +29,10 @@ jobs:
cargo clippy --target=x86_64-pc-windows-msvc --all-targets --color=always \
-- -D warnings
- run: |
cargo clippy --manifest-path fuzz/Cargo.toml --color=always \
cargo clippy --manifest-path fuzz/Cargo.toml --all-targets --color=always \
-- -D warnings
- run: |
cargo clippy --manifest-path bench/Cargo.toml --color=always \
cargo clippy --manifest-path bench/Cargo.toml --all-targets --color=always \
-- -D warnings
env:
RUSTFLAGS: "-Dwarnings"

View File

@ -51,7 +51,7 @@ js-sys = { version = "0.3", optional = true } # contains FFI bindings for
windows-targets = { version = "0.52", optional = true }
[target.'cfg(windows)'.dev-dependencies]
windows-bindgen = { version = "0.54" } # The MSRV of its windows-metatada 0.54 dependency is 1.70
windows-bindgen = { version = "0.55" } # The MSRV of its windows-metatada dependency is 1.70
[target.'cfg(unix)'.dependencies]
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }

View File

@ -3,7 +3,7 @@
//! ISO 8601 date and time with time zone.
#[cfg(all(not(feature = "std"), feature = "alloc"))]
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
use alloc::string::String;
use core::borrow::Borrow;
use core::cmp::Ordering;
@ -46,7 +46,7 @@ mod tests;
/// There are some constructors implemented here (the `from_*` methods), but
/// the general-purpose constructors are all via the methods on the
/// [`TimeZone`](./offset/trait.TimeZone.html) implementations.
#[derive(Clone)]
#[derive(Copy, Clone)]
#[cfg_attr(
any(feature = "rkyv", feature = "rkyv-16", feature = "rkyv-32", feature = "rkyv-64"),
derive(Archive, Deserialize, Serialize),
@ -1260,10 +1260,6 @@ impl<Tz: TimeZone> Timelike for DateTime<Tz> {
}
}
// we need them as automatic impls cannot handle associated types
impl<Tz: TimeZone> Copy for DateTime<Tz> where <Tz as TimeZone>::Offset: Copy {}
unsafe impl<Tz: TimeZone> Send for DateTime<Tz> where <Tz as TimeZone>::Offset: Send {}
impl<Tz: TimeZone, Tz2: TimeZone> PartialEq<DateTime<Tz2>> for DateTime<Tz> {
fn eq(&self, other: &DateTime<Tz2>) -> bool {
self.datetime == other.datetime

View File

@ -3,7 +3,7 @@
//! Date and time formatting routines.
#[cfg(all(not(feature = "std"), feature = "alloc"))]
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
use alloc::string::{String, ToString};
#[cfg(feature = "alloc")]
use core::borrow::Borrow;
@ -20,8 +20,6 @@ use crate::{NaiveDate, NaiveTime, Weekday};
#[cfg(feature = "alloc")]
use super::locales;
#[cfg(all(feature = "unstable-locales", feature = "alloc"))]
use super::Locale;
#[cfg(any(feature = "alloc", feature = "serde", feature = "rustc-serialize"))]
use super::{Colons, OffsetFormat, OffsetPrecision, Pad};
#[cfg(feature = "alloc")]

View File

@ -30,7 +30,7 @@
//! # Ok::<(), chrono::ParseError>(())
//! ```
#[cfg(all(not(feature = "std"), feature = "alloc"))]
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
use alloc::boxed::Box;
use core::fmt;
use core::str::FromStr;

View File

@ -165,7 +165,7 @@ use super::{locales, Locale};
use super::{Fixed, InternalInternal, Item, Numeric, Pad};
#[cfg(any(feature = "alloc", feature = "std"))]
use super::{ParseError, BAD_FORMAT};
#[cfg(feature = "alloc")]
#[cfg(all(feature = "alloc", not(feature = "std"), not(test)))]
use alloc::vec::Vec;
/// Parsing iterator for `strftime`-like format strings.

View File

@ -442,98 +442,102 @@ fn test_date_pred() {
}
#[test]
fn test_date_add() {
fn check((y1, m1, d1): (i32, u32, u32), rhs: TimeDelta, ymd: Option<(i32, u32, u32)>) {
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd(y, m, d).unwrap());
assert_eq!(lhs.checked_add_signed(rhs), sum);
assert_eq!(lhs.checked_sub_signed(-rhs), sum);
fn test_date_checked_add_signed() {
fn check(lhs: Option<NaiveDate>, delta: TimeDelta, rhs: Option<NaiveDate>) {
assert_eq!(lhs.unwrap().checked_add_signed(delta), rhs);
assert_eq!(lhs.unwrap().checked_sub_signed(-delta), rhs);
}
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
check((2014, 1, 1), TimeDelta::zero(), Some((2014, 1, 1)));
check((2014, 1, 1), TimeDelta::seconds(86399).unwrap(), Some((2014, 1, 1)));
check(ymd(2014, 1, 1), TimeDelta::zero(), ymd(2014, 1, 1));
check(ymd(2014, 1, 1), TimeDelta::seconds(86399).unwrap(), ymd(2014, 1, 1));
// always round towards zero
check((2014, 1, 1), TimeDelta::seconds(-86399).unwrap(), Some((2014, 1, 1)));
check((2014, 1, 1), TimeDelta::days(1).unwrap(), Some((2014, 1, 2)));
check((2014, 1, 1), TimeDelta::days(-1).unwrap(), Some((2013, 12, 31)));
check((2014, 1, 1), TimeDelta::days(364).unwrap(), Some((2014, 12, 31)));
check((2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap(), Some((2018, 1, 1)));
check((2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap(), Some((2414, 1, 1)));
check(ymd(2014, 1, 1), TimeDelta::seconds(-86399).unwrap(), ymd(2014, 1, 1));
check(ymd(2014, 1, 1), TimeDelta::days(1).unwrap(), ymd(2014, 1, 2));
check(ymd(2014, 1, 1), TimeDelta::days(-1).unwrap(), ymd(2013, 12, 31));
check(ymd(2014, 1, 1), TimeDelta::days(364).unwrap(), ymd(2014, 12, 31));
check(ymd(2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap(), ymd(2018, 1, 1));
check(ymd(2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap(), ymd(2414, 1, 1));
check((-7, 1, 1), TimeDelta::days(365 * 12 + 3).unwrap(), Some((5, 1, 1)));
check(ymd(-7, 1, 1), TimeDelta::days(365 * 12 + 3).unwrap(), ymd(5, 1, 1));
// overflow check
check(
(0, 1, 1),
ymd(0, 1, 1),
TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
Some((MAX_YEAR, 12, 31)),
ymd(MAX_YEAR, 12, 31),
);
check((0, 1, 1), TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None);
check((0, 1, 1), TimeDelta::max_value(), None);
check((0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(), Some((MIN_YEAR, 1, 1)));
check((0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None);
check((0, 1, 1), TimeDelta::min_value(), None);
check(ymd(0, 1, 1), TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64 + 1).unwrap(), None);
check(ymd(0, 1, 1), TimeDelta::max_value(), None);
check(ymd(0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap(), ymd(MIN_YEAR, 1, 1));
check(ymd(0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64 - 1).unwrap(), None);
check(ymd(0, 1, 1), TimeDelta::min_value(), None);
}
#[test]
fn test_date_sub() {
fn check((y1, m1, d1): (i32, u32, u32), (y2, m2, d2): (i32, u32, u32), diff: TimeDelta) {
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
let rhs = NaiveDate::from_ymd(y2, m2, d2).unwrap();
assert_eq!(lhs.signed_duration_since(rhs), diff);
assert_eq!(rhs.signed_duration_since(lhs), -diff);
fn test_date_signed_duration_since() {
fn check(lhs: Option<NaiveDate>, rhs: Option<NaiveDate>, delta: TimeDelta) {
assert_eq!(lhs.unwrap().signed_duration_since(rhs.unwrap()), delta);
assert_eq!(rhs.unwrap().signed_duration_since(lhs.unwrap()), -delta);
}
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
check((2014, 1, 1), (2014, 1, 1), TimeDelta::zero());
check((2014, 1, 2), (2014, 1, 1), TimeDelta::days(1).unwrap());
check((2014, 12, 31), (2014, 1, 1), TimeDelta::days(364).unwrap());
check((2015, 1, 3), (2014, 1, 1), TimeDelta::days(365 + 2).unwrap());
check((2018, 1, 1), (2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap());
check((2414, 1, 1), (2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap());
check(ymd(2014, 1, 1), ymd(2014, 1, 1), TimeDelta::zero());
check(ymd(2014, 1, 2), ymd(2014, 1, 1), TimeDelta::days(1).unwrap());
check(ymd(2014, 12, 31), ymd(2014, 1, 1), TimeDelta::days(364).unwrap());
check(ymd(2015, 1, 3), ymd(2014, 1, 1), TimeDelta::days(365 + 2).unwrap());
check(ymd(2018, 1, 1), ymd(2014, 1, 1), TimeDelta::days(365 * 4 + 1).unwrap());
check(ymd(2414, 1, 1), ymd(2014, 1, 1), TimeDelta::days(365 * 400 + 97).unwrap());
check((MAX_YEAR, 12, 31), (0, 1, 1), TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap());
check((MIN_YEAR, 1, 1), (0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap());
check(
ymd(MAX_YEAR, 12, 31),
ymd(0, 1, 1),
TimeDelta::days(MAX_DAYS_FROM_YEAR_0 as i64).unwrap(),
);
check(ymd(MIN_YEAR, 1, 1), ymd(0, 1, 1), TimeDelta::days(MIN_DAYS_FROM_YEAR_0 as i64).unwrap());
}
#[test]
fn test_date_add_days() {
fn check((y1, m1, d1): (i32, u32, u32), rhs: Days, ymd: Option<(i32, u32, u32)>) {
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
let sum = ymd.map(|(y, m, d)| NaiveDate::from_ymd(y, m, d).unwrap());
assert_eq!(lhs.checked_add_days(rhs), sum);
fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
assert_eq!(lhs.unwrap().checked_add_days(days), rhs);
}
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
check((2014, 1, 1), Days::new(0), Some((2014, 1, 1)));
check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
// always round towards zero
check((2014, 1, 1), Days::new(1), Some((2014, 1, 2)));
check((2014, 1, 1), Days::new(364), Some((2014, 12, 31)));
check((2014, 1, 1), Days::new(365 * 4 + 1), Some((2018, 1, 1)));
check((2014, 1, 1), Days::new(365 * 400 + 97), Some((2414, 1, 1)));
check(ymd(2014, 1, 1), Days::new(1), ymd(2014, 1, 2));
check(ymd(2014, 1, 1), Days::new(364), ymd(2014, 12, 31));
check(ymd(2014, 1, 1), Days::new(365 * 4 + 1), ymd(2018, 1, 1));
check(ymd(2014, 1, 1), Days::new(365 * 400 + 97), ymd(2414, 1, 1));
check((-7, 1, 1), Days::new(365 * 12 + 3), Some((5, 1, 1)));
check(ymd(-7, 1, 1), Days::new(365 * 12 + 3), ymd(5, 1, 1));
// overflow check
check((0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), Some((MAX_YEAR, 12, 31)));
check((0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None);
check(ymd(0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(MAX_YEAR, 12, 31));
check(ymd(0, 1, 1), Days::new(u64::try_from(MAX_DAYS_FROM_YEAR_0).unwrap() + 1), None);
}
#[test]
fn test_date_sub_days() {
fn check((y1, m1, d1): (i32, u32, u32), (y2, m2, d2): (i32, u32, u32), diff: Days) {
let lhs = NaiveDate::from_ymd(y1, m1, d1).unwrap();
let rhs = NaiveDate::from_ymd(y2, m2, d2).unwrap();
assert_eq!(lhs - diff, rhs);
fn check(lhs: Option<NaiveDate>, days: Days, rhs: Option<NaiveDate>) {
assert_eq!(lhs.unwrap().checked_sub_days(days), rhs);
}
let ymd = |y, m, d| NaiveDate::from_ymd(y, m, d).ok();
check((2014, 1, 1), (2014, 1, 1), Days::new(0));
check((2014, 1, 2), (2014, 1, 1), Days::new(1));
check((2014, 12, 31), (2014, 1, 1), Days::new(364));
check((2015, 1, 3), (2014, 1, 1), Days::new(365 + 2));
check((2018, 1, 1), (2014, 1, 1), Days::new(365 * 4 + 1));
check((2414, 1, 1), (2014, 1, 1), Days::new(365 * 400 + 97));
check(ymd(2014, 1, 1), Days::new(0), ymd(2014, 1, 1));
check(ymd(2014, 1, 2), Days::new(1), ymd(2014, 1, 1));
check(ymd(2014, 12, 31), Days::new(364), ymd(2014, 1, 1));
check(ymd(2015, 1, 3), Days::new(365 + 2), ymd(2014, 1, 1));
check(ymd(2018, 1, 1), Days::new(365 * 4 + 1), ymd(2014, 1, 1));
check(ymd(2414, 1, 1), Days::new(365 * 400 + 97), ymd(2014, 1, 1));
check((MAX_YEAR, 12, 31), (0, 1, 1), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()));
check((0, 1, 1), (MIN_YEAR, 1, 1), Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()));
check(ymd(MAX_YEAR, 12, 31), Days::new(MAX_DAYS_FROM_YEAR_0.try_into().unwrap()), ymd(0, 1, 1));
check(
ymd(0, 1, 1),
Days::new((-MIN_DAYS_FROM_YEAR_0).try_into().unwrap()),
ymd(MIN_YEAR, 1, 1),
);
}
#[test]

View File

@ -1,10 +1,8 @@
// Bindings generated by `windows-bindgen` 0.54.0
#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types, dead_code, clippy::all)]
::windows_targets::link!("kernel32.dll" "system" fn GetTimeZoneInformationForYear(wyear : u16, pdtzi : *const DYNAMIC_TIME_ZONE_INFORMATION, ptzi : *mut TIME_ZONE_INFORMATION) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn SystemTimeToFileTime(lpsystemtime : *const SYSTEMTIME, lpfiletime : *mut FILETIME) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn SystemTimeToTzSpecificLocalTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lpuniversaltime : *const SYSTEMTIME, lplocaltime : *mut SYSTEMTIME) -> BOOL);
::windows_targets::link!("kernel32.dll" "system" fn TzSpecificLocalTimeToSystemTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lplocaltime : *const SYSTEMTIME, lpuniversaltime : *mut SYSTEMTIME) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn GetTimeZoneInformationForYear(wyear : u16, pdtzi : *const DYNAMIC_TIME_ZONE_INFORMATION, ptzi : *mut TIME_ZONE_INFORMATION) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn SystemTimeToFileTime(lpsystemtime : *const SYSTEMTIME, lpfiletime : *mut FILETIME) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn SystemTimeToTzSpecificLocalTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lpuniversaltime : *const SYSTEMTIME, lplocaltime : *mut SYSTEMTIME) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn TzSpecificLocalTimeToSystemTime(lptimezoneinformation : *const TIME_ZONE_INFORMATION, lplocaltime : *const SYSTEMTIME, lpuniversaltime : *mut SYSTEMTIME) -> BOOL);
pub type BOOL = i32;
pub type BOOLEAN = u8;
#[repr(C)]
@ -19,8 +17,8 @@ pub struct DYNAMIC_TIME_ZONE_INFORMATION {
pub TimeZoneKeyName: [u16; 128],
pub DynamicDaylightTimeDisabled: BOOLEAN,
}
impl ::core::marker::Copy for DYNAMIC_TIME_ZONE_INFORMATION {}
impl ::core::clone::Clone for DYNAMIC_TIME_ZONE_INFORMATION {
impl Copy for DYNAMIC_TIME_ZONE_INFORMATION {}
impl Clone for DYNAMIC_TIME_ZONE_INFORMATION {
fn clone(&self) -> Self {
*self
}
@ -30,8 +28,8 @@ pub struct FILETIME {
pub dwLowDateTime: u32,
pub dwHighDateTime: u32,
}
impl ::core::marker::Copy for FILETIME {}
impl ::core::clone::Clone for FILETIME {
impl Copy for FILETIME {}
impl Clone for FILETIME {
fn clone(&self) -> Self {
*self
}
@ -47,8 +45,8 @@ pub struct SYSTEMTIME {
pub wSecond: u16,
pub wMilliseconds: u16,
}
impl ::core::marker::Copy for SYSTEMTIME {}
impl ::core::clone::Clone for SYSTEMTIME {
impl Copy for SYSTEMTIME {}
impl Clone for SYSTEMTIME {
fn clone(&self) -> Self {
*self
}
@ -63,8 +61,8 @@ pub struct TIME_ZONE_INFORMATION {
pub DaylightDate: SYSTEMTIME,
pub DaylightBias: i32,
}
impl ::core::marker::Copy for TIME_ZONE_INFORMATION {}
impl ::core::clone::Clone for TIME_ZONE_INFORMATION {
impl Copy for TIME_ZONE_INFORMATION {}
impl Clone for TIME_ZONE_INFORMATION {
fn clone(&self) -> Self {
*self
}

View File

@ -1,5 +1,5 @@
--out src/offset/local/win_bindings.rs
--config flatten sys
--config flatten sys no-bindgen-comment
--filter
Windows.Win32.System.Time.GetTimeZoneInformationForYear
Windows.Win32.System.Time.SystemTimeToFileTime

View File

@ -9,7 +9,6 @@
// except according to those terms.
use std::cmp::Ordering;
use std::convert::TryFrom;
use std::mem::MaybeUninit;
use std::ptr;
@ -141,18 +140,38 @@ impl TzInfo {
}
tz_info.assume_init()
};
let std_offset = (tz_info.Bias)
.checked_add(tz_info.StandardBias)
.and_then(|o| o.checked_mul(60))
.and_then(|o| FixedOffset::west(o).ok())?;
let dst_offset = (tz_info.Bias)
.checked_add(tz_info.DaylightBias)
.and_then(|o| o.checked_mul(60))
.and_then(|o| FixedOffset::west(o).ok())?;
Some(TzInfo {
std_offset: FixedOffset::west((tz_info.Bias + tz_info.StandardBias) * 60).ok()?,
dst_offset: FixedOffset::west((tz_info.Bias + tz_info.DaylightBias) * 60).ok()?,
std_transition: system_time_from_naive_date_time(tz_info.StandardDate, year),
dst_transition: system_time_from_naive_date_time(tz_info.DaylightDate, year),
std_offset,
dst_offset,
std_transition: naive_date_time_from_system_time(tz_info.StandardDate, year).ok()?,
dst_transition: naive_date_time_from_system_time(tz_info.DaylightDate, year).ok()?,
})
}
}
fn system_time_from_naive_date_time(st: SYSTEMTIME, year: i32) -> Option<NaiveDateTime> {
/// Resolve a `SYSTEMTIME` object to an `Option<NaiveDateTime>`.
///
/// A `SYSTEMTIME` within a `TIME_ZONE_INFORMATION` struct can be zero to indicate there is no
/// transition.
/// If it has year, month and day values it is a concrete date.
/// If the year is missing the `SYSTEMTIME` is a rule, which this method resolves for the provided
/// year. A rule has a month, weekday, and nth weekday of the month as components.
///
/// Returns `Err` if any of the values is invalid, which should never happen.
fn naive_date_time_from_system_time(
st: SYSTEMTIME,
year: i32,
) -> Result<Option<NaiveDateTime>, ()> {
if st.wYear == 0 && st.wMonth == 0 {
return None; // No DST transitions for this year in this timezone.
return Ok(None);
}
let time = NaiveTime::from_hms_milli(
st.wHour as u32,
@ -160,25 +179,35 @@ fn system_time_from_naive_date_time(st: SYSTEMTIME, year: i32) -> Option<NaiveDa
st.wSecond as u32,
st.wMilliseconds as u32,
)
.ok()?;
// In Chrono's Weekday, Monday is 0 whereas in SYSTEMTIME Monday is 1 and Sunday is 0.
// Therefore we move back one day after converting the u16 value to a Weekday.
let day_of_week = Weekday::try_from(u8::try_from(st.wDayOfWeek).ok()?).ok()?.pred();
.map_err(|_| ())?;
if st.wYear != 0 {
return NaiveDate::from_ymd(st.wYear as i32, st.wMonth as u32, st.wDay as u32)
.ok()
.map(|d| d.and_time(time));
// We have a concrete date.
let date = NaiveDate::from_ymd(st.wYear as i32, st.wMonth as u32, st.wDay as u32)
.map_err(|_| ())?;
return Ok(Some(date.and_time(time)));
}
let date = if let Some(date) =
NaiveDate::from_weekday_of_month(year, st.wMonth as u32, day_of_week, st.wDay as u8)
{
date
} else if st.wDay == 5 {
NaiveDate::from_weekday_of_month(year, st.wMonth as u32, day_of_week, 4)?
} else {
return None;
// Resolve a rule with month, weekday, and nth weekday of the month to a date in the current
// year.
let weekday = match st.wDayOfWeek {
0 => Weekday::Sun,
1 => Weekday::Mon,
2 => Weekday::Tue,
3 => Weekday::Wed,
4 => Weekday::Thu,
5 => Weekday::Fri,
6 => Weekday::Sat,
_ => return Err(()),
};
Some(date.and_time(time))
let nth_day = match st.wDay {
1..=5 => st.wDay as u8,
_ => return Err(()),
};
let date = NaiveDate::from_weekday_of_month(year, st.wMonth as u32, weekday, nth_day)
.or_else(|| NaiveDate::from_weekday_of_month(year, st.wMonth as u32, weekday, 4))
.ok_or(())?; // `st.wMonth` must be invalid
Ok(Some(date.and_time(time)))
}
#[cfg(test)]

View File

@ -6,7 +6,6 @@
use crate::{DateTime, NaiveDateTime, TimeDelta, TimeZone, Timelike};
use core::cmp::Ordering;
use core::fmt;
use core::marker::Sized;
use core::ops::{Add, Sub};
/// Extension trait for subsecond rounding or truncation to a maximum number