mirror of
https://github.com/chronotope/chrono.git
synced 2025-10-03 15:56:48 +00:00
factor calculations to weeks_from function and add tests
tests for weeks_from and num_days_from fix array iter MSRV issue
This commit is contained in:
parent
8197700ccd
commit
cf2a2f95f7
@ -520,12 +520,8 @@ fn format_inner(
|
|||||||
Item::Numeric(ref spec, ref pad) => {
|
Item::Numeric(ref spec, ref pad) => {
|
||||||
use self::Numeric::*;
|
use self::Numeric::*;
|
||||||
|
|
||||||
let week_from_sun = |d: &NaiveDate| {
|
let week_from_sun = |d: &NaiveDate| d.weeks_from(Weekday::Sun);
|
||||||
(d.ordinal() as i32 - d.weekday().num_days_from_sunday() as i32 + 6) / 7
|
let week_from_mon = |d: &NaiveDate| d.weeks_from(Weekday::Mon);
|
||||||
};
|
|
||||||
let week_from_mon = |d: &NaiveDate| {
|
|
||||||
(d.ordinal() as i32 - d.weekday().num_days_from_monday() as i32 + 6) / 7
|
|
||||||
};
|
|
||||||
|
|
||||||
let (width, v) = match *spec {
|
let (width, v) = match *spec {
|
||||||
Year => (4, date.map(|d| i64::from(d.year()))),
|
Year => (4, date.map(|d| i64::from(d.year()))),
|
||||||
|
@ -379,9 +379,8 @@ impl Parsed {
|
|||||||
// verify the ordinal and other (non-ISO) week dates.
|
// verify the ordinal and other (non-ISO) week dates.
|
||||||
let verify_ordinal = |date: NaiveDate| {
|
let verify_ordinal = |date: NaiveDate| {
|
||||||
let ordinal = date.ordinal();
|
let ordinal = date.ordinal();
|
||||||
let weekday = date.weekday();
|
let week_from_sun = date.weeks_from(Weekday::Sun);
|
||||||
let week_from_sun = (ordinal as i32 - weekday.num_days_from_sunday() as i32 + 6) / 7;
|
let week_from_mon = date.weeks_from(Weekday::Mon);
|
||||||
let week_from_mon = (ordinal as i32 - weekday.num_days_from_monday() as i32 + 6) / 7;
|
|
||||||
self.ordinal.unwrap_or(ordinal) == ordinal
|
self.ordinal.unwrap_or(ordinal) == ordinal
|
||||||
&& self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun
|
&& self.week_from_sun.map_or(week_from_sun, |v| v as i32) == week_from_sun
|
||||||
&& self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon
|
&& self.week_from_mon.map_or(week_from_mon, |v| v as i32) == week_from_mon
|
||||||
|
@ -236,6 +236,9 @@ fn test_date_bounds() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NaiveDate {
|
impl NaiveDate {
|
||||||
|
pub(crate) fn weeks_from(&self, day: Weekday) -> i32 {
|
||||||
|
(self.ordinal() as i32 - self.weekday().num_days_from(day) as i32 + 6) / 7
|
||||||
|
}
|
||||||
/// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification.
|
/// Makes a new `NaiveDate` from year and packed ordinal-flags, with a verification.
|
||||||
fn from_of(year: i32, of: Of) -> Option<NaiveDate> {
|
fn from_of(year: i32, of: Of) -> Option<NaiveDate> {
|
||||||
if (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() {
|
if (MIN_YEAR..=MAX_YEAR).contains(&year) && of.valid() {
|
||||||
@ -2928,4 +2931,67 @@ mod tests {
|
|||||||
assert!(days.contains(&date));
|
assert!(days.contains(&date));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_weeks_from() {
|
||||||
|
// tests per: https://github.com/chronotope/chrono/issues/961
|
||||||
|
// these internally use `weeks_from` via the parsing infrastructure
|
||||||
|
assert_eq!(
|
||||||
|
NaiveDate::parse_from_str("2020-01-0", "%Y-%W-%w").ok(),
|
||||||
|
NaiveDate::from_ymd_opt(2020, 1, 12),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
NaiveDate::parse_from_str("2019-01-0", "%Y-%W-%w").ok(),
|
||||||
|
NaiveDate::from_ymd_opt(2019, 1, 13),
|
||||||
|
);
|
||||||
|
|
||||||
|
// direct tests
|
||||||
|
for (y, starts_on) in &[
|
||||||
|
(2019, Weekday::Tue),
|
||||||
|
(2020, Weekday::Wed),
|
||||||
|
(2021, Weekday::Fri),
|
||||||
|
(2022, Weekday::Sat),
|
||||||
|
(2023, Weekday::Sun),
|
||||||
|
(2024, Weekday::Mon),
|
||||||
|
(2025, Weekday::Wed),
|
||||||
|
(2026, Weekday::Thu),
|
||||||
|
] {
|
||||||
|
for day in &[
|
||||||
|
Weekday::Mon,
|
||||||
|
Weekday::Tue,
|
||||||
|
Weekday::Wed,
|
||||||
|
Weekday::Thu,
|
||||||
|
Weekday::Fri,
|
||||||
|
Weekday::Sat,
|
||||||
|
Weekday::Sun,
|
||||||
|
] {
|
||||||
|
assert_eq!(
|
||||||
|
NaiveDate::from_ymd_opt(*y, 1, 1).map(|d| d.weeks_from(*day)),
|
||||||
|
Some(if day == starts_on { 1 } else { 0 })
|
||||||
|
);
|
||||||
|
|
||||||
|
// last day must always be in week 52 or 53
|
||||||
|
assert!([52, 53]
|
||||||
|
.contains(&NaiveDate::from_ymd_opt(*y, 12, 31).unwrap().weeks_from(*day)),);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = NaiveDate::from_ymd_opt(2019, 1, 1).unwrap();
|
||||||
|
|
||||||
|
// 400 years covers all year types
|
||||||
|
for day in &[
|
||||||
|
Weekday::Mon,
|
||||||
|
Weekday::Tue,
|
||||||
|
Weekday::Wed,
|
||||||
|
Weekday::Thu,
|
||||||
|
Weekday::Fri,
|
||||||
|
Weekday::Sat,
|
||||||
|
Weekday::Sun,
|
||||||
|
] {
|
||||||
|
// must always be below 54
|
||||||
|
for dplus in 1..(400 * 366) {
|
||||||
|
assert!((base + Days::new(dplus)).weeks_from(*day) < 54)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,15 +73,7 @@ impl Weekday {
|
|||||||
/// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
/// `w.number_from_monday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn number_from_monday(&self) -> u32 {
|
pub fn number_from_monday(&self) -> u32 {
|
||||||
match *self {
|
self.num_days_from(Weekday::Mon) + 1
|
||||||
Weekday::Mon => 1,
|
|
||||||
Weekday::Tue => 2,
|
|
||||||
Weekday::Wed => 3,
|
|
||||||
Weekday::Thu => 4,
|
|
||||||
Weekday::Fri => 5,
|
|
||||||
Weekday::Sat => 6,
|
|
||||||
Weekday::Sun => 7,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a day-of-week number starting from Sunday = 1.
|
/// Returns a day-of-week number starting from Sunday = 1.
|
||||||
@ -91,15 +83,7 @@ impl Weekday {
|
|||||||
/// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1
|
/// `w.number_from_sunday()`: | 2 | 3 | 4 | 5 | 6 | 7 | 1
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn number_from_sunday(&self) -> u32 {
|
pub fn number_from_sunday(&self) -> u32 {
|
||||||
match *self {
|
self.num_days_from(Weekday::Sun) + 1
|
||||||
Weekday::Mon => 2,
|
|
||||||
Weekday::Tue => 3,
|
|
||||||
Weekday::Wed => 4,
|
|
||||||
Weekday::Thu => 5,
|
|
||||||
Weekday::Fri => 6,
|
|
||||||
Weekday::Sat => 7,
|
|
||||||
Weekday::Sun => 1,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a day-of-week number starting from Monday = 0.
|
/// Returns a day-of-week number starting from Monday = 0.
|
||||||
@ -109,15 +93,7 @@ impl Weekday {
|
|||||||
/// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6
|
/// `w.num_days_from_monday()`: | 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn num_days_from_monday(&self) -> u32 {
|
pub fn num_days_from_monday(&self) -> u32 {
|
||||||
match *self {
|
self.num_days_from(Weekday::Mon)
|
||||||
Weekday::Mon => 0,
|
|
||||||
Weekday::Tue => 1,
|
|
||||||
Weekday::Wed => 2,
|
|
||||||
Weekday::Thu => 3,
|
|
||||||
Weekday::Fri => 4,
|
|
||||||
Weekday::Sat => 5,
|
|
||||||
Weekday::Sun => 6,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a day-of-week number starting from Sunday = 0.
|
/// Returns a day-of-week number starting from Sunday = 0.
|
||||||
@ -127,15 +103,17 @@ impl Weekday {
|
|||||||
/// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0
|
/// `w.num_days_from_sunday()`: | 1 | 2 | 3 | 4 | 5 | 6 | 0
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn num_days_from_sunday(&self) -> u32 {
|
pub fn num_days_from_sunday(&self) -> u32 {
|
||||||
match *self {
|
self.num_days_from(Weekday::Sun)
|
||||||
Weekday::Mon => 1,
|
|
||||||
Weekday::Tue => 2,
|
|
||||||
Weekday::Wed => 3,
|
|
||||||
Weekday::Thu => 4,
|
|
||||||
Weekday::Fri => 5,
|
|
||||||
Weekday::Sat => 6,
|
|
||||||
Weekday::Sun => 0,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.num_days_from(wd)`: | 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn num_days_from(&self, day: Weekday) -> u32 {
|
||||||
|
(*self as u32 + 7 - day as u32) % 7
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,6 +186,45 @@ impl fmt::Debug for ParseWeekdayError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use num_traits::FromPrimitive;
|
||||||
|
|
||||||
|
use super::Weekday;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_num_days_from() {
|
||||||
|
for i in 0..7 {
|
||||||
|
let base_day = Weekday::from_u64(i).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(base_day.num_days_from_monday(), base_day.num_days_from(Weekday::Mon));
|
||||||
|
assert_eq!(base_day.num_days_from_sunday(), base_day.num_days_from(Weekday::Sun));
|
||||||
|
|
||||||
|
assert_eq!(base_day.num_days_from(base_day), 0);
|
||||||
|
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.pred()), 1);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.pred().pred()), 2);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred()), 3);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred()), 4);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.pred().pred().pred().pred().pred()), 5);
|
||||||
|
assert_eq!(
|
||||||
|
base_day.num_days_from(base_day.pred().pred().pred().pred().pred().pred()),
|
||||||
|
6
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.succ()), 6);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.succ().succ()), 5);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ()), 4);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ()), 3);
|
||||||
|
assert_eq!(base_day.num_days_from(base_day.succ().succ().succ().succ().succ()), 2);
|
||||||
|
assert_eq!(
|
||||||
|
base_day.num_days_from(base_day.succ().succ().succ().succ().succ().succ()),
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
|
// the actual `FromStr` implementation is in the `format` module to leverage the existing code
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user