Skip to content

Commit 748e154

Browse files
committed
add MonthsDelta
1 parent d147e9a commit 748e154

File tree

3 files changed

+108
-3
lines changed

3 files changed

+108
-3
lines changed

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ pub mod prelude {
419419
#[doc(no_inline)]
420420
pub use crate::Locale;
421421
#[doc(no_inline)]
422+
pub use crate::MonthsDelta;
423+
#[doc(no_inline)]
422424
pub use crate::SubsecRound;
423425
#[doc(no_inline)]
424426
pub use crate::{DateTime, SecondsFormat};
@@ -466,7 +468,7 @@ mod weekday;
466468
pub use weekday::{ParseWeekdayError, Weekday};
467469

468470
mod month;
469-
pub use month::{Month, Months, ParseMonthError};
471+
pub use month::{Month, Months, MonthsDelta, ParseMonthError};
470472

471473
mod traits;
472474
pub use traits::{Datelike, Timelike};

src/month.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ impl TryFrom<u8> for Month {
176176
}
177177

178178
/// A duration in calendar months
179-
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
179+
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
180180
pub struct Months(pub(crate) u32);
181181

182182
impl Months {
@@ -186,6 +186,66 @@ impl Months {
186186
}
187187
}
188188

189+
/// A difference in a number of months, either forwards or backwards.
190+
///
191+
/// This type is often returned from fuctions but is generally not used as a parameter.
192+
/// Instead the inner `Months` must first be extracted and then used. There are helper methods
193+
/// which can assist with this.
194+
#[derive(Clone, Copy, Debug, PartialOrd, Ord)]
195+
pub enum MonthsDelta {
196+
/// the forwards direction
197+
Forwards(Months),
198+
/// the backwards direction
199+
Backwards(Months),
200+
}
201+
202+
impl MonthsDelta {
203+
/// Assert that the direction is forwards and throw away the `Months` otherwise.
204+
pub fn forwards(self) -> Option<Months> {
205+
match self {
206+
MonthsDelta::Forwards(d) => Some(d),
207+
MonthsDelta::Backwards(_) => None,
208+
}
209+
}
210+
211+
/// Assert that the direction is backwards and throw away the `Months` otherwise.
212+
pub fn backwards(self) -> Option<Months> {
213+
match self {
214+
MonthsDelta::Forwards(_) => None,
215+
MonthsDelta::Backwards(d) => Some(d),
216+
}
217+
}
218+
219+
/// Get the contained `Months`, no matter which direction
220+
pub fn abs(self) -> Months {
221+
match self {
222+
MonthsDelta::Backwards(d) => d,
223+
MonthsDelta::Forwards(d) => d,
224+
}
225+
}
226+
}
227+
228+
impl PartialEq for MonthsDelta {
229+
fn eq(&self, other: &MonthsDelta) -> bool {
230+
match (self, other) {
231+
(MonthsDelta::Forwards(f1), MonthsDelta::Forwards(f2)) => f1 == f2,
232+
(MonthsDelta::Backwards(b1), MonthsDelta::Backwards(b2)) => b1 == b2,
233+
(MonthsDelta::Forwards(lhs), MonthsDelta::Backwards(rhs))
234+
| (MonthsDelta::Backwards(lhs), MonthsDelta::Forwards(rhs)) => {
235+
*lhs == Months(0) && *rhs == Months(0)
236+
}
237+
}
238+
}
239+
}
240+
241+
impl Eq for MonthsDelta {}
242+
243+
impl From<Months> for MonthsDelta {
244+
fn from(s: Months) -> Self {
245+
MonthsDelta::Forwards(s)
246+
}
247+
}
248+
189249
/// An error resulting from reading `<Month>` value with `FromStr`.
190250
#[derive(Clone, PartialEq, Eq)]
191251
pub struct ParseMonthError {

src/naive/date.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rkyv::{Archive, Deserialize, Serialize};
1717
use crate::format::DelayedFormat;
1818
use crate::format::{parse, ParseError, ParseResult, Parsed, StrftimeItems};
1919
use crate::format::{Item, Numeric, Pad};
20-
use crate::month::Months;
20+
use crate::month::{Months, MonthsDelta};
2121
use crate::naive::{IsoWeek, NaiveDateTime, NaiveTime};
2222
use crate::{Datelike, TimeDelta, Weekday};
2323

@@ -1042,6 +1042,49 @@ impl NaiveDate {
10421042
)
10431043
}
10441044

1045+
/// Subtracts another `NaiveDate` from the current date.
1046+
/// Returns a `MonthsDelta` of the number of calendar months between the two dates.
1047+
///
1048+
/// # Examples
1049+
///
1050+
/// ```
1051+
/// use chrono::{NaiveDate, MonthsDelta, Months};
1052+
///
1053+
/// assert_eq!(
1054+
/// NaiveDate::from_ymd(2022, 4, 18).months_since(NaiveDate::from_ymd(2022, 4, 1)),
1055+
/// MonthsDelta::Forwards(Months::new(0))
1056+
/// );
1057+
/// assert_eq!(
1058+
/// NaiveDate::from_ymd(2022, 5, 1).months_since(NaiveDate::from_ymd(2022, 4, 30)),
1059+
/// MonthsDelta::Forwards(Months::new(1))
1060+
/// );
1061+
/// assert_eq!(
1062+
/// NaiveDate::from_ymd(2023, 5, 1).months_since(NaiveDate::from_ymd(2022, 4, 30)),
1063+
/// MonthsDelta::Forwards(Months::new(13))
1064+
/// );
1065+
/// assert_eq!(
1066+
/// NaiveDate::from_ymd(2022, 4, 1).months_since(NaiveDate::from_ymd(2022, 4, 18)),
1067+
/// MonthsDelta::Backwards(Months::new(0))
1068+
/// );
1069+
/// assert_eq!(
1070+
/// NaiveDate::from_ymd(2022, 4, 30).months_since(NaiveDate::from_ymd(2022, 5, 1)),
1071+
/// MonthsDelta::Backwards(Months::new(1))
1072+
/// );
1073+
/// assert_eq!(
1074+
/// NaiveDate::from_ymd(2022, 4, 30).months_since(NaiveDate::from_ymd(2023, 5, 1)),
1075+
/// MonthsDelta::Backwards(Months::new(13))
1076+
/// );
1077+
/// ```
1078+
pub fn months_since(self, rhs: NaiveDate) -> MonthsDelta {
1079+
if self >= rhs {
1080+
let years = u32::try_from(self.year() - rhs.year()).expect("Will succeed");
1081+
MonthsDelta::Forwards(Months::new(years * 12 + self.month() - rhs.month()))
1082+
} else {
1083+
let years = u32::try_from((rhs.year() - self.year()).abs()).expect("Will succeed");
1084+
MonthsDelta::Backwards(Months::new(years * 12 + rhs.month() - self.month()))
1085+
}
1086+
}
1087+
10451088
/// Formats the date with the specified formatting items.
10461089
/// Otherwise it is the same as the ordinary `format` method.
10471090
///

0 commit comments

Comments
 (0)