@@ -9,6 +9,8 @@ use crate::{Range, Tendril};
10use chrono::{Datelike, Duration, NaiveDate};
10use chrono::{Datelike, Duration, NaiveDate};
12fn ndays_in_month(year: i32, month: u32) -> u32 {
14fn ndays_in_month(year: i32, month: u32) -> u32 {
13 // The first day of the next month...
15 // The first day of the next month...
14 let (y, m) = if month == 12 {
16 let (y, m) = if month == 12 {
@@ -29,6 +31,7 @@ fn add_days(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
29fn add_months(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
31fn add_months(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
30 let month = date.month0() as i64 + amount;
32 let month = date.month0() as i64 + amount;
31 let year = date.year() + i32::try_from(month / 12).ok()?;
33 let year = date.year() + i32::try_from(month / 12).ok()?;
34+ let year = if month.is_negative() { year - 1 } else { year };
33 // Normalize month
36 // Normalize month
34 let month = month % 12;
37 let month = month % 12;
@@ -45,7 +48,6 @@ fn add_months(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
46fn add_years(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
49fn add_years(date: NaiveDate, amount: i64) -> Option<NaiveDate> {
47 let year = i32::try_from(date.year() as i64 + amount).ok()?;
50 let year = i32::try_from(date.year() as i64 + amount).ok()?;
49 let ndays = ndays_in_month(year, date.month());
51 let ndays = ndays_in_month(year, date.month());
51 if date.day() > ndays {
53 if date.day() > ndays {
@@ -85,9 +87,8 @@ enum DateField {
86#[derive(Debug, PartialEq, Eq)]
88#[derive(Debug, PartialEq, Eq)]
87pub struct DateIncrementor {
89pub struct DateIncrementor {
88− pub date: NaiveDate,
90+ date: NaiveDate,
89− pub range: Range,
91+ range: Range,
91 field: DateField,
92 field: DateField,
92 format: Format,
93 format: Format,
@@ -150,8 +151,10 @@ impl DateIncrementor {
156+impl Increment for DateIncrementor {
154− pub fn incremented_text(&self, amount: i64) -> Tendril {
157+ fn increment(&self, amount: i64) -> (Range, Tendril) {
155 let date = match self.field {
158 let date = match self.field {
156 DateField::Year => add_years(self.date, amount),
159 DateField::Year => add_years(self.date, amount),
157 DateField::Month => add_months(self.date, amount),
160 DateField::Month => add_months(self.date, amount),
@@ -159,15 +162,18 @@ impl DateIncrementor {
160 .unwrap_or(self.date);
163 .unwrap_or(self.date);
162− format!(
167+ format!(
163− "{:04}{}{:02}{}{:02}",
168+ "{:04}{}{:02}{}{:02}",
164− date.year(),
169+ date.year(),
165− self.format.separator,
170+ self.format.separator,
166− date.month(),
171+ date.month(),
167− self.format.separator,
172+ self.format.separator,
168− date.day()
173+ date.day()
@@ -437,6 +443,8 @@ mod test {
437 ("2020-02-29", 0, 1, "2021-03-01"),
443 ("2020-02-29", 0, 1, "2021-03-01"),
438 ("2020-01-31", 5, 1, "2020-02-29"),
444 ("2020-01-31", 5, 1, "2020-02-29"),
439 ("2020-01-20", 5, 1, "2020-02-20"),
445 ("2020-01-20", 5, 1, "2020-02-20"),
446+ ("2021-01-01", 5, -1, "2020-12-01"),
447+ ("2021-01-31", 5, -2, "2020-11-30"),
440 ("2020-02-28", 8, 1, "2020-02-29"),
448 ("2020-02-28", 8, 1, "2020-02-29"),
441 ("2021-02-28", 8, 1, "2021-03-01"),
449 ("2021-02-28", 8, 1, "2021-03-01"),
442 ("2021-02-28", 0, -1, "2020-02-28"),
450 ("2021-02-28", 0, -1, "2020-02-28"),
@@ -457,7 +465,8 @@ mod test {
457 assert_eq!(
465 assert_eq!(
458 DateIncrementor::from_range(rope.slice(..), range)
466 DateIncrementor::from_range(rope.slice(..), range)
459 .unwrap()
467 .unwrap()
460− .incremented_text(amount),
468+ .increment(amount)
461 expected.into()
470 expected.into()