JavaScriptで年月日をループ処理する

# はじめに
日付をループ処理する機会があったため自身の備忘録として簡単にまとめておきたいと思います。
今回はfor文で配列を作成しようと思います。

# 日間をループさせる
今日は2023年1月1日ですので2022年12月1日から今日までの約1ヶ月間の日付配列を作ってみます。

実装したコードは以下の通りです。

```js
const now = new Date();
const OLDEST_DATE = new Date(2022, 11, 1)

const resultDates = [];

for (let date = OLDEST_DATE; date <= now; date.setDate(date.getDate() + 1)) {
    resultDates.push(new Date(date));
}
```
<br>

まず初めに"now"として今日の日付を、"OLDEST_DATE"でループを開始する始めの日付を設定しておきます。
```js
const now = new Date(); // 今日の日付
const OLDEST_DATE = new Date(2022, 11, 1) // ループを開始する日付
```
この際Date()のMonthは0~11で設定する必要があるためMonth-1の数字を設定することに注意しましょう。
<br>

配列は予め"dates"として空の配列を定義しておきます。

for文は"date"が"now"以下かで比較します。
変化式では、現在設定されている日付の「日」を返す**getDate**に1を加算し、**setDate**で設定します。
```js
for (let date = OLDEST_DATE; date <= now; date.setDate(date.getDate() + 1))
```
<br>

console.logした結果が以下になります。
```yaml
// console.log(resultDates)
[
  2022-12-01T00:00:00.000Z, 2022-12-02T00:00:00.000Z,
  2022-12-03T00:00:00.000Z, 2022-12-04T00:00:00.000Z,
  2022-12-05T00:00:00.000Z, 2022-12-06T00:00:00.000Z,
  2022-12-07T00:00:00.000Z, 2022-12-08T00:00:00.000Z,
  2022-12-09T00:00:00.000Z, 2022-12-10T00:00:00.000Z,
  2022-12-11T00:00:00.000Z, 2022-12-12T00:00:00.000Z,
  2022-12-13T00:00:00.000Z, 2022-12-14T00:00:00.000Z,
  2022-12-15T00:00:00.000Z, 2022-12-16T00:00:00.000Z,
  2022-12-17T00:00:00.000Z, 2022-12-18T00:00:00.000Z,
  2022-12-19T00:00:00.000Z, 2022-12-20T00:00:00.000Z,
  2022-12-21T00:00:00.000Z, 2022-12-22T00:00:00.000Z,
  2022-12-23T00:00:00.000Z, 2022-12-24T00:00:00.000Z,
  2022-12-25T00:00:00.000Z, 2022-12-26T00:00:00.000Z,
  2022-12-27T00:00:00.000Z, 2022-12-28T00:00:00.000Z,
  2022-12-29T00:00:00.000Z, 2022-12-30T00:00:00.000Z,
  2022-12-31T00:00:00.000Z, 2023-01-01T00:00:00.000Z
]
```
31日で月が変わり1月1日として格納されているのが分かると思います。
必要に応じて配列にpushする際フォーマットして格納してください。

# 月間をループさせる
月間でループさせる場合もほぼ同様の方法で実装ができます。
```js
const now = new Date();
const OLDEST_MONTH = new Date(2022, 01)

const resultMonth = [];

for (let month = OLDEST_MONTH; month <= now; month.setMonth(month.getMonth() + 1)) {
    resultMonth.push(new Date(month));
}
```
今回は月間ですので**getMonth**で取得し、**setMonth**で設定します。

console.logした結果が以下になります。
```yaml
// console.log(resultMonth)
[
  2022-02-01T00:00:00.000Z,
  2022-03-01T00:00:00.000Z,
  2022-04-01T00:00:00.000Z,
  2022-05-01T00:00:00.000Z,
  2022-06-01T00:00:00.000Z,
  2022-07-01T00:00:00.000Z,
  2022-08-01T00:00:00.000Z,
  2022-09-01T00:00:00.000Z,
  2022-10-01T00:00:00.000Z,
  2022-11-01T00:00:00.000Z,
  2022-12-01T00:00:00.000Z,
  2023-01-01T00:00:00.000Z
]
```

# 年間をループさせる
年間でループさせる場合は少し注意が必要です。
```js
const now = new Date();
const OLDEST_YEAR = new Date(2010, 01)

const resultYear = [];
for (let year = OLDEST_YEAR; year <= now; year.setFullYear(year.getFullYear() + 1)) {
    resultYear.push(new Date(year));
}
```
<br>

まず必ず日付は少なくとも月までは入力しましょう。
月を入力しない場合正しく動作しません。
```js
const OLDEST_YEAR = new Date(2010, 00, 01) // OK
const OLDEST_YEAR = new Date(2010, 00) // OK
const OLDEST_YEAR = new Date(2010) // NG
```
<br>

getDate、getMonthときて、getYearと書きたくなりますが、年の取得は**getFullYear**を必ず使いましょう。
getYearは2桁の年を取得する関数でgetFullYearが4桁の年を取得する関数です。
```js
for (let year = OLDEST_YEAR; year <= now; year.setFullYear(year.getFullYear() + 1))
```
set側についてはsetYear()で4桁を設定することもできますが非推奨のため、同様に**setFullYear()** を使うのがいいでしょう。

# おわりに
年月日のループは自身で実装しようとすると意外と手間の掛かる処理なので、同じ様な実装で悩んでいる方がいれば参考になると嬉しいです。

# 追記
コメントでdayjsを使った実装方法も教えて頂いたので共有させていただきます。
dayjsは日付と時刻を解析、検証、操作、および表示するJavaScriptライブラリです。

https://day.js.org/en/
```ts
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration.js";

dayjs.extend(duration);

const NOW = dayjs().startOf("month");
const OLDEST_DAY = dayjs("2022-12-01");
const OLDEST_MONTH = dayjs("2022-01");
const OLDEST_YEAR = dayjs("2010");

const doLoop = (base, unit) => {
  const calcDuration = (duration) => {
    switch (unit) {
      case "days":
        return duration.asDays();
      case "months":
        return duration.asMonths();
      case "years":
        return duration.asYears();
      default:
        break;
    }
  };
  return [
    ...Array(Math.ceil(calcDuration(dayjs.duration(NOW.diff(base))))),
  ].map((_, i) => NOW.add(i, unit).format("YYYY-MM-DD"));
};

console.log(doLoop(OLDEST_DAY, "days"));
console.log(doLoop(OLDEST_MONTH, "months"));
console.log(doLoop(OLDEST_YEAR, "years"));
```
実行結果
```
$ time node index.js
[
  '2023-01-01', '2023-01-02', '2023-01-03',
  '2023-01-04', '2023-01-05', '2023-01-06',
  '2023-01-07', '2023-01-08', '2023-01-09',
  '2023-01-10', '2023-01-11', '2023-01-12',
  '2023-01-13', '2023-01-14', '2023-01-15',
  '2023-01-16', '2023-01-17', '2023-01-18',
  '2023-01-19', '2023-01-20', '2023-01-21',
  '2023-01-22', '2023-01-23', '2023-01-24',
  '2023-01-25', '2023-01-26', '2023-01-27',
  '2023-01-28', '2023-01-29', '2023-01-30',
  '2023-01-31'
]
[
  '2023-01-01', '2023-02-01',
  '2023-03-01', '2023-04-01',
  '2023-05-01', '2023-06-01',
  '2023-07-01', '2023-08-01',
  '2023-09-01', '2023-10-01',
  '2023-11-01', '2023-12-01',
  '2024-01-01'
]
[
  '2023-01-01', '2024-01-01',
  '2025-01-01', '2026-01-01',
  '2027-01-01', '2028-01-01',
  '2029-01-01', '2030-01-01',
  '2031-01-01', '2032-01-01',
  '2033-01-01', '2034-01-01',
  '2035-01-01', '2036-01-01'
]

real    0m0.038s
user    0m0.049s
sys     0m0.000s
```
是非参考にしてみてください。