diff --git a/src/naive/date.rs b/src/naive/date.rs index 5a0551b475..a56e3628da 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -25,7 +25,8 @@ use crate::format::{ use crate::month::Months; use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime}; use crate::oldtime::Duration as OldDuration; -use crate::{Datelike, Duration, Weekday}; +use crate::{expect, try_opt}; +use crate::{Datelike, Weekday}; use super::internals::{self, DateImpl, Mdf, Of, YearFlags}; use super::isoweek; @@ -67,7 +68,7 @@ impl NaiveWeek { // Do not construct an intermediate date beyond `self.date`, because that may be out of // range if `date` is close to `NaiveDate::MAX`. let days = start - ref_day - if start > ref_day { 7 } else { 0 }; - self.date.diff_days(days as i64).unwrap() + self.date.add_days(days).unwrap() } /// Returns a date representing the last day of the week. @@ -95,7 +96,7 @@ impl NaiveWeek { // Do not construct an intermediate date before `self.date` (like with `first_day()`), // because that may be out of range if `date` is close to `NaiveDate::MIN`. let days = end - ref_day + if end < ref_day { 7 } else { 0 }; - self.date.diff_days(days as i64).unwrap() + self.date.add_days(days).unwrap() } /// Returns a [`RangeInclusive`] representing the whole week bounded by @@ -255,8 +256,8 @@ impl NaiveDate { /// or if `year` is out of range for `NaiveDate`. #[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") + pub const fn from_ymd(year: i32, month: u32, day: u32) -> NaiveDate { + expect!(NaiveDate::from_ymd_opt(year, month, day), "invalid or out-of-range date") } /// Makes a new `NaiveDate` from the [calendar date](#calendar-date) @@ -303,8 +304,8 @@ impl NaiveDate { /// `year` is out of range for `NaiveDate`. #[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") + pub const fn from_yo(year: i32, ordinal: u32) -> NaiveDate { + expect!(NaiveDate::from_yo_opt(year, ordinal), "invalid or out-of-range date") } /// Makes a new `NaiveDate` from the [ordinal date](#ordinal-date) @@ -333,7 +334,7 @@ impl NaiveDate { /// assert!(from_yo_opt(-400000, 1).is_none()); /// ``` #[must_use] - pub fn from_yo_opt(year: i32, ordinal: u32) -> Option { + pub const fn from_yo_opt(year: i32, ordinal: u32) -> Option { let flags = YearFlags::from_year(year); NaiveDate::from_ordinal_and_flags(year, ordinal, flags) } @@ -348,8 +349,8 @@ impl NaiveDate { /// if the resulting date is out of range for `NaiveDate`. #[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") + pub const fn from_isoywd(year: i32, week: u32, weekday: Weekday) -> NaiveDate { + expect!(NaiveDate::from_isoywd_opt(year, week, weekday), "invalid or out-of-range date") } /// Makes a new `NaiveDate` from the [ISO week date](#week-date) @@ -402,7 +403,7 @@ impl NaiveDate { /// 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 { + pub const fn from_isoywd_opt(year: i32, week: u32, weekday: Weekday) -> Option { let flags = YearFlags::from_year(year); let nweeks = flags.nisoweeks(); if 1 <= week && week <= nweeks { @@ -443,8 +444,8 @@ impl NaiveDate { #[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") + pub const fn from_num_days_from_ce(days: i32) -> NaiveDate { + expect!(NaiveDate::from_num_days_from_ce_opt(days), "out-of-range date") } /// Makes a new `NaiveDate` from a day's number in the proleptic Gregorian calendar, with @@ -470,9 +471,10 @@ impl NaiveDate { /// assert_eq!(from_ndays_opt(-100_000_000), None); /// ``` #[must_use] - pub fn from_num_days_from_ce_opt(days: i32) -> Option { - 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); + pub const fn from_num_days_from_ce_opt(days: i32) -> Option { + let days = try_opt!(days.checked_add(365)); // make December 31, 1 BCE equal to day 0 + let year_div_400 = days.div_euclid(146_097); + let cycle = days.rem_euclid(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); NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) @@ -490,8 +492,13 @@ impl NaiveDate { /// `n`, or if `year` is out of range for `NaiveDate`. #[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") + pub const fn from_weekday_of_month( + year: i32, + month: u32, + weekday: Weekday, + n: u8, + ) -> NaiveDate { + expect!(NaiveDate::from_weekday_of_month_opt(year, month, weekday, n), "out-of-range date") } /// Makes a new `NaiveDate` by counting the number of occurrences of a particular day-of-week @@ -515,7 +522,7 @@ impl NaiveDate { /// NaiveDate::from_ymd_opt(2017, 3, 10)) /// ``` #[must_use] - pub fn from_weekday_of_month_opt( + pub const fn from_weekday_of_month_opt( year: i32, month: u32, weekday: Weekday, @@ -524,9 +531,9 @@ impl NaiveDate { if n == 0 { return None; } - let first = NaiveDate::from_ymd_opt(year, month, 1)?.weekday(); + let first = try_opt!(NaiveDate::from_ymd_opt(year, month, 1)).weekday(); let first_to_dow = (7 + weekday.number_from_monday() - first.number_from_monday()) % 7; - let day = (u32::from(n) - 1) * 7 + first_to_dow + 1; + let day = (n - 1) as u32 * 7 + first_to_dow + 1; NaiveDate::from_ymd_opt(year, month, day) } @@ -622,7 +629,7 @@ impl NaiveDate { /// ); /// ``` #[must_use] - pub fn checked_add_months(self, months: Months) -> Option { + pub const fn checked_add_months(self, months: Months) -> Option { if months.0 == 0 { return Some(self); } @@ -657,7 +664,7 @@ impl NaiveDate { /// ); /// ``` #[must_use] - pub fn checked_sub_months(self, months: Months) -> Option { + pub const fn checked_sub_months(self, months: Months) -> Option { if months.0 == 0 { return Some(self); } @@ -669,7 +676,7 @@ impl NaiveDate { } } - fn diff_months(self, months: i32) -> Option { + const fn diff_months(self, months: i32) -> Option { let (years, left) = ((months / 12), (months % 12)); // Determine new year (without taking months into account for now @@ -706,9 +713,13 @@ impl NaiveDate { let flags = YearFlags::from_year(year); let feb_days = if flags.ndays() == 366 { 29 } else { 28 }; let days = [31, feb_days, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - let day = Ord::min(self.day(), days[(month - 1) as usize]); + let day_max = days[(month - 1) as usize]; + let mut day = self.day(); + if day > day_max { + day = day_max; + }; - NaiveDate::from_mdf(year, Mdf::new(month as u32, day, flags)?) + NaiveDate::from_mdf(year, try_opt!(Mdf::new(month as u32, day, flags))) } /// Add a duration in [`Days`] to the date @@ -735,12 +746,11 @@ impl NaiveDate { /// ); /// ``` #[must_use] - pub fn checked_add_days(self, days: Days) -> Option { - if days.0 == 0 { - return Some(self); + pub const fn checked_add_days(self, days: Days) -> Option { + match days.0 <= i32::MAX as u64 { + true => self.add_days(days.0 as i32), + false => None, } - - i64::try_from(days.0).ok().and_then(|d| self.diff_days(d)) } /// Subtract a duration in [`Days`] from the date @@ -763,20 +773,28 @@ impl NaiveDate { /// ); /// ``` #[must_use] - pub fn checked_sub_days(self, days: Days) -> Option { - if days.0 == 0 { - return Some(self); + pub const fn checked_sub_days(self, days: Days) -> Option { + match days.0 <= i32::MAX as u64 { + true => self.add_days(-(days.0 as i32)), + false => None, } - - i64::try_from(days.0).ok().and_then(|d| self.diff_days(-d)) } - fn diff_days(self, days: i64) -> Option { - let secs = days.checked_mul(86400)?; // 86400 seconds in one day - if secs >= core::i64::MAX / 1000 || secs <= core::i64::MIN / 1000 { - return None; // See the `time` 0.1 crate. Outside these bounds, `Duration::seconds` will panic + /// Add a duration of `i32` days to the date. + pub(crate) const fn add_days(self, days: i32) -> Option { + if days == 0 { + return Some(self); } - self.checked_add_signed(Duration::seconds(secs)) + let year = self.year(); + let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); + let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); + let cycle = try_opt!((cycle as i32).checked_add(days)); + let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); + year_div_400 += cycle_div_400y; + + let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); + let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); + NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) } /// Makes a new `NaiveDateTime` from the current date and given `NaiveTime`. @@ -810,8 +828,8 @@ impl NaiveDate { #[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") + pub const fn and_hms(&self, hour: u32, min: u32, sec: u32) -> NaiveDateTime { + expect!(self.and_hms_opt(hour, min, sec), "invalid time") } /// Makes a new `NaiveDateTime` from the current date, hour, minute and second. @@ -836,8 +854,9 @@ impl NaiveDate { /// ``` #[inline] #[must_use] - pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option { - NaiveTime::from_hms_opt(hour, min, sec).map(|time| self.and_time(time)) + pub const fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option { + let time = try_opt!(NaiveTime::from_hms_opt(hour, min, sec)); + Some(self.and_time(time)) } /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. @@ -851,8 +870,8 @@ impl NaiveDate { #[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") + pub const fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> NaiveDateTime { + expect!(self.and_hms_milli_opt(hour, min, sec, milli), "invalid time") } /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and millisecond. @@ -879,14 +898,15 @@ impl NaiveDate { /// ``` #[inline] #[must_use] - pub fn and_hms_milli_opt( + pub const fn and_hms_milli_opt( &self, hour: u32, min: u32, sec: u32, milli: u32, ) -> Option { - NaiveTime::from_hms_milli_opt(hour, min, sec, milli).map(|time| self.and_time(time)) + let time = try_opt!(NaiveTime::from_hms_milli_opt(hour, min, sec, milli)); + Some(self.and_time(time)) } /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. @@ -914,8 +934,8 @@ 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") + pub const fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> NaiveDateTime { + expect!(self.and_hms_micro_opt(hour, min, sec, micro), "invalid time") } /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and microsecond. @@ -942,14 +962,15 @@ impl NaiveDate { /// ``` #[inline] #[must_use] - pub fn and_hms_micro_opt( + pub const fn and_hms_micro_opt( &self, hour: u32, min: u32, sec: u32, micro: u32, ) -> Option { - NaiveTime::from_hms_micro_opt(hour, min, sec, micro).map(|time| self.and_time(time)) + let time = try_opt!(NaiveTime::from_hms_micro_opt(hour, min, sec, micro)); + Some(self.and_time(time)) } /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. @@ -963,8 +984,8 @@ impl NaiveDate { #[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") + pub const fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> NaiveDateTime { + expect!(self.and_hms_nano_opt(hour, min, sec, nano), "invalid time") } /// Makes a new `NaiveDateTime` from the current date, hour, minute, second and nanosecond. @@ -991,19 +1012,20 @@ impl NaiveDate { /// ``` #[inline] #[must_use] - pub fn and_hms_nano_opt( + pub const fn and_hms_nano_opt( &self, hour: u32, min: u32, sec: u32, nano: u32, ) -> Option { - NaiveTime::from_hms_nano_opt(hour, min, sec, nano).map(|time| self.and_time(time)) + let time = try_opt!(NaiveTime::from_hms_nano_opt(hour, min, sec, nano)); + Some(self.and_time(time)) } /// Returns the packed month-day-flags. #[inline] - fn mdf(&self) -> Mdf { + const fn mdf(&self) -> Mdf { self.of().to_mdf() } @@ -1017,8 +1039,8 @@ impl NaiveDate { /// /// Returns `None` when the resulting `NaiveDate` would be invalid. #[inline] - fn with_mdf(&self, mdf: Mdf) -> Option { - Some(self.with_of(mdf.to_of()?)) + const fn with_mdf(&self, mdf: Mdf) -> Option { + Some(self.with_of(try_opt!(mdf.to_of()))) } /// Makes a new `NaiveDate` with the packed ordinal-flags changed. @@ -1038,8 +1060,8 @@ impl NaiveDate { #[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") + pub const fn succ(&self) -> NaiveDate { + expect!(self.succ_opt(), "out of bound") } /// Makes a new `NaiveDate` for the next calendar date. @@ -1059,7 +1081,7 @@ impl NaiveDate { /// ``` #[inline] #[must_use] - pub fn succ_opt(&self) -> Option { + pub const fn succ_opt(&self) -> Option { match self.of().succ() { Some(of) => Some(self.with_of(of)), None => NaiveDate::from_ymd_opt(self.year() + 1, 1, 1), @@ -1074,8 +1096,8 @@ impl NaiveDate { #[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") + pub const fn pred(&self) -> NaiveDate { + expect!(self.pred_opt(), "out of bound") } /// Makes a new `NaiveDate` for the previous calendar date. @@ -1095,7 +1117,7 @@ impl NaiveDate { /// ``` #[inline] #[must_use] - pub fn pred_opt(&self) -> Option { + pub const fn pred_opt(&self) -> Option { match self.of().pred() { Some(of) => Some(self.with_of(of)), None => NaiveDate::from_ymd_opt(self.year() - 1, 12, 31), @@ -1124,16 +1146,8 @@ impl NaiveDate { /// ``` #[must_use] pub fn checked_add_signed(self, rhs: OldDuration) -> Option { - let year = self.year(); - let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); - let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); - let cycle = (cycle as i32).checked_add(i32::try_from(rhs.num_days()).ok()?)?; - let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); - year_div_400 += cycle_div_400y; - - let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); - let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) + let days = i32::try_from(rhs.num_days()).ok()?; + self.add_days(days) } /// Subtracts the number of whole days in the given `Duration` from the current date. @@ -1158,16 +1172,8 @@ impl NaiveDate { /// ``` #[must_use] pub fn checked_sub_signed(self, rhs: OldDuration) -> Option { - let year = self.year(); - let (mut year_div_400, year_mod_400) = div_mod_floor(year, 400); - let cycle = internals::yo_to_cycle(year_mod_400 as u32, self.of().ordinal()); - let cycle = (cycle as i32).checked_sub(i32::try_from(rhs.num_days()).ok()?)?; - let (cycle_div_400y, cycle) = div_mod_floor(cycle, 146_097); - year_div_400 += cycle_div_400y; - - let (year_mod_400, ordinal) = internals::cycle_to_yo(cycle as u32); - let flags = YearFlags::from_year_mod_400(year_mod_400 as i32); - NaiveDate::from_ordinal_and_flags(year_div_400 * 400 + year_mod_400 as i32, ordinal, flags) + let days = i32::try_from(-rhs.num_days()).ok()?; + self.add_days(days) } /// Subtracts another `NaiveDate` from the current date. @@ -1198,10 +1204,10 @@ impl NaiveDate { let year2 = rhs.year(); let (year1_div_400, year1_mod_400) = div_mod_floor(year1, 400); let (year2_div_400, year2_mod_400) = div_mod_floor(year2, 400); - let cycle1 = i64::from(internals::yo_to_cycle(year1_mod_400 as u32, self.of().ordinal())); - let cycle2 = i64::from(internals::yo_to_cycle(year2_mod_400 as u32, rhs.of().ordinal())); + let cycle1 = internals::yo_to_cycle(year1_mod_400 as u32, self.of().ordinal()) as i64; + let cycle2 = internals::yo_to_cycle(year2_mod_400 as u32, rhs.of().ordinal()) as i64; OldDuration::days( - (i64::from(year1_div_400) - i64::from(year2_div_400)) * 146_097 + (cycle1 - cycle2), + (year1_div_400 as i64 - year2_div_400 as i64) * 146_097 + (cycle1 - cycle2), ) } @@ -1211,9 +1217,11 @@ impl NaiveDate { /// /// Returns `None` if `base < self`. #[must_use] - pub fn years_since(&self, base: Self) -> Option { + pub const fn years_since(&self, base: Self) -> Option { let mut years = self.year() - base.year(); - if (self.month(), self.day()) < (base.month(), base.day()) { + // Comparing tuples is not (yet) possible in const context. Instead we combine month and + // day into one `u32` for easy comparison. + if (self.month() << 5 | self.day()) < (base.month() << 5 | base.day()) { years -= 1; } @@ -1404,6 +1412,30 @@ impl NaiveDate { NaiveWeek { date: *self, start } } + // This duplicates `Datelike::year()`, because trait methods can't be const yet. + #[inline] + const fn year(&self) -> i32 { + self.ymdf >> 13 + } + + // This duplicates `Datelike::month()`, because trait methods can't be const yet. + #[inline] + const fn month(&self) -> u32 { + self.mdf().month() + } + + // This duplicates `Datelike::day()`, because trait methods can't be const yet. + #[inline] + const fn day(&self) -> u32 { + self.mdf().day() + } + + // This duplicates `Datelike::weekday()`, because trait methods can't be const yet. + #[inline] + const fn weekday(&self) -> Weekday { + self.of().weekday() + } + /// The minimum possible `NaiveDate` (January 1, 262145 BCE). pub const MIN: NaiveDate = NaiveDate { ymdf: (MIN_YEAR << 13) | (1 << 4) | 0o07 /*FE*/ }; /// The maximum possible `NaiveDate` (December 31, 262143 CE). @@ -1423,7 +1455,7 @@ impl Datelike for NaiveDate { /// ``` #[inline] fn year(&self) -> i32 { - self.ymdf >> 13 + self.year() } /// Returns the month number starting from 1. @@ -1440,7 +1472,7 @@ impl Datelike for NaiveDate { /// ``` #[inline] fn month(&self) -> u32 { - self.mdf().month() + self.month() } /// Returns the month number starting from 0. @@ -1457,7 +1489,7 @@ impl Datelike for NaiveDate { /// ``` #[inline] fn month0(&self) -> u32 { - self.mdf().month() - 1 + self.month() - 1 } /// Returns the day of month starting from 1. @@ -1497,7 +1529,7 @@ impl Datelike for NaiveDate { /// ``` #[inline] fn day(&self) -> u32 { - self.mdf().day() + self.day() } /// Returns the day of month starting from 0. @@ -1585,7 +1617,7 @@ impl Datelike for NaiveDate { /// ``` #[inline] fn weekday(&self) -> Weekday { - self.of().weekday() + self.weekday() } #[inline] @@ -2153,7 +2185,7 @@ impl Default for NaiveDate { } } -fn div_mod_floor(val: i32, div: i32) -> (i32, i32) { +const fn div_mod_floor(val: i32, div: i32) -> (i32, i32) { (val.div_euclid(div), val.rem_euclid(div)) } diff --git a/src/naive/datetime/mod.rs b/src/naive/datetime/mod.rs index 6fa9bb31fb..78d6d0a57f 100644 --- a/src/naive/datetime/mod.rs +++ b/src/naive/datetime/mod.rs @@ -955,7 +955,7 @@ impl NaiveDateTime { impl Datelike for NaiveDateTime { /// Returns the year number in the [calendar date](./struct.NaiveDate.html#calendar-date). /// - /// See also the [`NaiveDate::year`] method. + /// See also the [`NaiveDate::year`](./struct.NaiveDate.html#method.year) method. /// /// # Example ///