如何在 Chrono NaiveDate 上增加一个月?

10

我正在尝试创建一个日期选择器,并且需要在月份之间进行导航。

let current = NaiveDate::parse_from_str("2020-10-15", "%Y-%m-%d").unwrap();

我该如何生成日期 2020-11-15

4个回答

5

这是我的解决方案:

use chrono::{ NaiveDate, Duration, Datelike};

fn get_days_from_month(year: i32, month: u32) -> u32 {
    NaiveDate::from_ymd(
        match month {
            12 => year + 1,
            _ => year,
        },
        match month {
            12 => 1,
            _ => month + 1,
        },
        1,
    )
    .signed_duration_since(NaiveDate::from_ymd(year, month, 1))
    .num_days() as u32
}

fn add_months(date: NaiveDate, num_months: u32) -> NaiveDate {
    let mut month = date.month() + num_months;
    let year = date.year() + (month / 12) as i32;
    month = month % 12;
    let mut day = date.day();
    let max_days = get_days_from_month(year, month);
    day = if day > max_days { max_days } else { day };
    NaiveDate::from_ymd(year, month, day)
}

fn main() {
    let date = NaiveDate::parse_from_str("2020-10-31", "%Y-%m-%d").unwrap();
    let new_date = add_months(date, 4);
    println!("{:?}", new_date);
}

3

NaiveDate现在有一个方法可以实现这个功能:checked_add_months

assert_eq!(
    NaiveDate::from_ymd(2020, 10, 15).checked_add_months(chrono::Months::new(1)),
    Some(NaiveDate::from_ymd(2020, 11, 15))
);
assert_eq!(
    NaiveDate::from_ymd(2020, 10, 15).checked_add_months(chrono::Months::new(3)),
    Some(NaiveDate::from_ymd(2021, 1, 15))
);

2

尝试使用 chronoutil crate。其中有 chronoutil::delta::shift_months 函数适用于任何 Datelike

    let current = NaiveDate::parse_from_str("2020-10-15", "%Y-%m-%d").unwrap();
    let next = shift_months(current, 1);

    assert_eq!(NaiveDate::parse_from_str("2020-11-15", "%Y-%m-%d").unwrap(), next);

注意:

如果月末日期存在歧义,则会向后移动。

所以对于2020-1-30,它会给出2020-2-29,而2021-1-30则变为2021-2-28。

    let current = NaiveDate::parse_from_str("2021-1-30", "%Y-%m-%d").unwrap();
    let next = shift_months(current, 1);

    assert_eq!(NaiveDate::parse_from_str("2021-2-28", "%Y-%m-%d").unwrap(), next);

1
您实际上在询问两件不同的事情。要进入下一个月并非与将一个月添加到日期相同,因为后者还必须考虑日期的有效性。例如,如果您将一个月添加到1月29日,则通常会到达3月而不是2月,因为通常没有2月29日,除了闰年。
仅涉及年份和月份要简单得多,因为每年的月份数量都是相同的。因此,为了回答如何在日期选择器中导航月份的根本问题,这里是答案:
fn next_month(year: i32, month: u32) -> (i32, u32) {
  assert!(month >= 1 && month <= 12);

  if month == 12 {
    (year + 1, 1)
  } else {
    (year, month + 1)
  }
}

fn prev_month(year: i32, month: u32) -> (i32, u32) {
  assert!(month >= 1 && month <= 12);

  if month == 1 {
    (year - 1, 12)
  } else {
    (year, month - 1)
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接