diff --git a/src/traits.rs b/src/traits.rs index bc1fee4f40..079db53d83 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,6 +1,33 @@ use crate::{IsoWeek, Weekday}; /// The common set of methods for date component. +/// +/// Methods such as [`year`], [`month`], [`day`] and [`weekday`] can be used to get basic +/// information about the date. +/// +/// The `with_*` methods can change the date. +/// +/// # Warning +/// +/// The `with_*` methods can be convenient to change a single component of a date, but they must be +/// used with some care. Examples to watch out for: +/// +/// - [`with_year`] changes the year component of a year-month-day value. Don't use this method if +/// you want the ordinal to stay the same after changing the year, of if you want the week and +/// weekday values to stay the same. +/// - Don't combine two `with_*` methods to change two components of the date. For example to +/// change both the year and month components of a date. This could fail because an intermediate +/// value does not exist, while the final date would be valid. +/// +/// For more complex changes to a date, it is best to use the methods on [`NaiveDate`] to create a +/// new value instead of altering an existing date. +/// +/// [`year`]: Datelike::year +/// [`month`]: Datelike::month +/// [`day`]: Datelike::day +/// [`weekday`]: Datelike::weekday +/// [`with_year`]: Datelike::with_year +/// [`NaiveDate`]: crate::NaiveDate pub trait Datelike: Sized { /// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date). fn year(&self) -> i32; @@ -55,6 +82,10 @@ pub trait Datelike: Sized { /// Makes a new value with the year number changed, while keeping the same month and day. /// + /// This method assumes you want to work on the date as a year-month-day value. Don't use it if + /// you want the ordinal to stay the same after changing the year, of if you want the week and + /// weekday values to stay the same. + /// /// # Errors /// /// Returns `None` when: @@ -66,6 +97,25 @@ pub trait Datelike: Sized { /// /// [`NaiveDate`]: crate::NaiveDate /// [`DateTime`]: crate::DateTime + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2020, 5, 13).unwrap().with_year(2023).unwrap(), + /// NaiveDate::from_ymd_opt(2023, 5, 13).unwrap() + /// ); + /// // Resulting date 2023-02-29 does not exist: + /// assert!(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap().with_year(2023).is_none()); + /// + /// // Don't use `with_year` if you want the ordinal date to stay the same: + /// assert_ne!( + /// NaiveDate::from_yo_opt(2020, 100).unwrap().with_year(2023).unwrap(), + /// NaiveDate::from_yo_opt(2023, 100).unwrap() // result is 2023-101 + /// ); + /// ``` fn with_year(&self, year: i32) -> Option; /// Makes a new value with the month number (starting from 1) changed. @@ -80,6 +130,37 @@ pub trait Datelike: Sized { /// - The value for `month` is out of range. /// /// [`DateTime`]: crate::DateTime + /// + /// # Examples + /// + /// ``` + /// use chrono::{NaiveDate, Datelike}; + /// + /// assert_eq!( + /// NaiveDate::from_ymd_opt(2023, 5, 12).unwrap().with_month(9).unwrap(), + /// NaiveDate::from_ymd_opt(2023, 9, 12).unwrap() + /// ); + /// // Resulting date 2023-09-31 does not exist: + /// assert!(NaiveDate::from_ymd_opt(2023, 5, 31).unwrap().with_month(9).is_none()); + /// ``` + /// + /// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist. + /// ``` + /// use chrono::{NaiveDate, Datelike}; + /// + /// fn with_year_month(date: NaiveDate, year: i32, month: u32) -> Option { + /// date.with_year(year)?.with_month(month) + /// } + /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(); + /// assert!(with_year_month(d, 2019, 1).is_none()); // fails because of invalid intermediate value + /// + /// // Correct version: + /// fn with_year_month_fixed(date: NaiveDate, year: i32, month: u32) -> Option { + /// NaiveDate::from_ymd_opt(year, month, date.day()) + /// } + /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap(); + /// assert_eq!(with_year_month_fixed(d, 2019, 1), NaiveDate::from_ymd_opt(2019, 1, 29)); + /// ``` fn with_month(&self, month: u32) -> Option; /// Makes a new value with the month number (starting from 0) changed.