小ネタ。
最近、Rustの勉強として小さいツールを書いている。そこで出くわしたものをイディオムとしてメモしておく。
記事を書いていたら日付の和・差のtrait実装まで調べることになったのでそれも書く。

やりたいこと

ある日付(start_date)からある日付(end_date)の間にある日付をすべて取得したい。

日時を扱うときはChronoというcrateがデファクトで使われるらしいのでこれを使う。

環境

  • Rust
    1.47.0
  • chrono
    0.4.19

コード

2020/11/01 から 2020/11/10 までの全日付を取得する。

Cargo.toml

dependenciesのところに追記。

[dependencies]
chrono = "0.4.19"

src/main.rs

iter_daysを使う。
これを使うと、指定した日からの日付イテレータが返る。それをtake_whileで打ち切ればいい。
今回の目的には超便利。

use chrono::prelude::*;
use chrono::Duration;

fn main() {
    let start_date = NaiveDate::from_ymd(2020, 11, 1);
    let end_date = NaiveDate::from_ymd(2020, 11, 10);
    let dates: Vec<NaiveDate> = start_date.iter_days().take_while(|x| x <= &end_date).collect();
    println!("{:?}", dates);
    // => [2020-11-01, 2020-11-02, 2020-11-03, 2020-11-04, 2020-11-05, 2020-11-06, 2020-11-07, 2020-11-08, 2020-11-09, 2020-11-10]
}

別の方法

end_date から start_date を引いた差(duration)を取って、それをnum_daysで日数に換算。
あとは日数をrangeにして、 map の中で start_date に加えていくという方法。
最初はこの方法を考えていた。

日付の差を取るのはsigned_duration_sinceを使えばよさそう。
単純に end_date - start_date でも同じ。

use chrono::prelude::*;
use chrono::Duration;

fn main() {
    let start_date = NaiveDate::from_ymd(2020, 11, 1);
    let end_date = NaiveDate::from_ymd(2020, 11, 10);
    let duration = end_date.signed_duration_since(start_date);// end_date - start_date でも同じ
    let dates: Vec<NaiveDate> = (0..=duration.num_days()).map(|x| start_date + Duration::days(x)).collect();
    println!("{:?}", dates);
    // => [2020-11-01, 2020-11-02, 2020-11-03, 2020-11-04, 2020-11-05, 2020-11-06, 2020-11-07, 2020-11-08, 2020-11-09, 2020-11-10]
}

なお日付の和は 日付 + Duration でやった。メソッドとしてはchecked_add_signedに対応しているが、
差のときと違って単純に置きかえはできない。
つまり

map(|x| start_date + Duration::days(x))

の部分を

map(|x| start_date.checked_add_signed(Duration::days(x)))

とはできない。なぜなら checked_add_signed はOptionを返すから。

map(|x| start_date.checked_add_signed(Duration::days(x)).expect("failed"))

としてOptionを外せば通る。

日付で +- を使ったときにどのメソッドが使われるのかはtrait実装を見ればいいようだ。
たとえば + はAdd traitの実装で、ここを見れば checked_add_signed に対応することがわかる。