RRULE 毎日、毎週、毎月、毎年の繰り返し日時を取得

# はじめに

予定の繰り返し情報として利用できそうな RRULE を各ケースごとに備忘録として残します。
※ 開始日時、終了日時、出力結果の時刻（HH:mm）部分は省略して記載しています。

- [Recurrence Rule | iCalendar (RFC 5545)](https://datatracker.ietf.org/doc/html/rfc5545#section-3.3.10)
- [rrule.js demo](https://jakubroztocil.github.io/rrule/) （RRULE の出力確認デモサイト）

# 毎日

```
// 開始日時: 2022/12/07（水）
// 間隔: 1日毎
// 終了回数: 5回
DTSTART:20221207T000000Z
RRULE:FREQ=DAILY;COUNT=10;INTERVAL=1;WKST=MO

// 出力結果
2022/12/7（水）
2022/12/8（木）
2022/12/9（金）
2022/12/10（土）
2022/12/11（日）
```

```
// 開始日時: 2022/12/07（水）
// 間隔: 2日毎
// 終了日時: 2022/12/13（火）
DTSTART:20221207T000000Z
RRULE:FREQ=DAILY;UNTIL=20221213T000000Z;INTERVAL=2;WKST=MO

// 出力結果
2022/12/7（水）
2022/12/9（金）
2022/12/11（日）
2022/12/13（火）
```

# 毎週

```
// 開始日時: 2022/12/07（水）
// 曜日: 月、水、金
// 間隔: 1週毎
// 終了回数: 5回
DTSTART:20221207T000000Z
RRULE:FREQ=WEEKLY;COUNT=5;INTERVAL=1;WKST=MO;BYDAY=MO,WE,FR

// 出力結果
2022/12/7（水）
2022/12/9（金）
2022/12/12（月）
2022/12/14（水）
2022/12/16（金）
```

```
// 開始日時: 2022/12/06（火）
// 曜日: 火、木
// 間隔: 2週毎
// 終了日: 2022/12/13（火）
DTSTART:20221206T000000Z
RRULE:FREQ=WEEKLY;UNTIL=20221213T000000Z;INTERVAL=2;WKST=MO;BYDAY=TU,TH

// 出力結果
2022/12/6（火）
2022/12/8（木）
2022/12/13（火）
```

## 終了回数を「〇週分」として指定したい場合

終了回数へ下記の値を指定します。
`{繰り返し週数} * {指定曜日数} - {日曜から開始日の曜日までに指定されている曜日数}(*1)`
\*1: 開始日の曜日が「水」の場合、「日、月、火」を指定している数

例）2022/12/07（水）から 3 週間分（12/4, 12/11, 12/18 の週）の曜日を指定したい

```
// 開始日時: 2022/12/07（水）
// 曜日: 月、水、金
// 間隔: 1週毎
// 終了回数: 8回 = 3（繰り返し週数） * 3（指定曜日数: 月、水、金） - 1（日曜から水曜までに指定されている曜日数: 月）
DTSTART:20221207T000000Z
RRULE:FREQ=WEEKLY;COUNT=8;INTERVAL=1;WKST=MO;BYDAY=MO,WE,FR

// 出力結果
2022/12/7（水）
2022/12/9（金）
2022/12/12（月）
2022/12/14（水）
2022/12/16（金）
2022/12/19（月）
2022/12/21（水）
2022/12/23（金）
```

（普通に三週間後の週末日付を UNTIL へ指定すれば良かった気も…）

# 毎月

```
// 開始日時: 2022/12/07（水）
// 月ごとの繰り返し: 開始日と同一日
// 間隔: 1月毎
// 終了回数: 5回
DTSTART:20221207T000000Z
RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO

// 出力結果
2022/12/7（水）
2023/1/7（土）
2023/2/7（火）
2023/3/7（火）
2023/4/7（金）
```

```
// 開始日時: 2022/12/07（水）
// 月ごとの繰り返し: 開始日と同一の第〇△曜日（第1水曜日）
// 間隔: 1月毎
// 終了回数: 5回
DTSTART:20221207T000000Z
RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO;BYDAY=WE;BYSETPOS=1

// 出力結果
2022/12/7（水）
2023/1/4（水）
2023/2/1（水）
2023/3/1（水）
2023/4/5（水）
```

※ **第 5**〇曜日を指定したい場合は、`BYSETPOS=-1` を指定する必要があります。
（第 5 〇曜日が存在しない月は、最終日の対象曜日を使用）

```
// 開始日時: 2022/12/29（木）
// 月ごとの繰り返し: 開始日と同一の第〇△曜日（第5木曜日）
// 間隔: 1月毎
// 終了回数: 5回
DTSTART:20221229T000000Z
RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO;BYDAY=TH;BYSETPOS=-1

// 出力結果
2022/12/29（木）
2023/1/26（木）
2023/2/23（木）
2023/3/30（木）
2023/4/27（木）
```

## ※ 注意: 開始日時が 〇月 29 日~31 日の場合

他の月に存在しない日付を指定した場合、対象の月がスキップされてピックアップされてしまいます。
対策として、`BYMONTHDAY=28,29,30,31`（開始日時が〇月 31 日の場合）、`BYSETPOS=-1` と指定し、
対象月の最大日付をピックアップします。
（開始日が 29 日,30 日,31 日 でも 2 月 28 日 をピックアップすることが可能）

- 開始日時: 28 日以前
  `RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO;`
- 開始日時: 〇月 29 日
  `RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO;BYSETPOS=-1;BYMONTHDAY=28,29`
- 開始日時: 〇月 30 日
  `RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO;BYSETPOS=-1;BYMONTHDAY=28,29,30`
- 開始日時: 〇月 31 日
  `RRULE:FREQ=MONTHLY;COUNT=5;INTERVAL=1;WKST=MO;BYSETPOS=-1;BYMONTHDAY=28,29,30,31`

# 毎年

```
// 開始日時: 2022/12/07（水）
// 年ごとの繰り返し: 開始日と同一日
// 間隔: 1年毎
// 終了回数: 5回
DTSTART:20221207T000000Z
RRULE:FREQ=YEARLY;COUNT=5;INTERVAL=1;WKST=MO

// 出力結果
2022/12/7（水）
2023/12/7（木）
2024/12/7（土）
2025/12/7（日）
2026/12/7（月）
```

```
// 開始日時: 2022/12/29（木）
// 年ごとの繰り返し: 開始日と同一の〇月第〇△曜日（12月第1木曜日）
// 間隔: 1年毎
// 終了回数: 5回
DTSTART:20221229T000000Z
RRULE:FREQ=YEARLY;COUNT=5;INTERVAL=1;WKST=MO;BYDAY=WE;BYMONTH=12;BYSETPOS=1

// 出力結果
2022/12/7（水）
2023/12/6（水）
2024/12/4（水）
2025/12/3（水）
2026/12/2（水）
```

```
// 開始日時: 2022/12/07（水）
// 年ごとの繰り返し: 開始日と同一の〇月第〇△曜日（12月第5水曜日）
// 間隔: 1年毎
// 終了回数: 5回
DTSTART:20221207T000000Z
RRULE:FREQ=YEARLY;COUNT=5;INTERVAL=1;WKST=MO;BYDAY=TH;BYMONTH=12;BYSETPOS=-1

// 出力結果
2022/12/29（木）
2023/12/28（木）
2024/12/26（木）
2025/12/25（木）
2026/12/31（木）
```

## ※ 注意: 開始日時が 2/29 うるう年（2024/2/29...等）の場合

他の年月に存在しない日付を指定した場合、対象の年月がスキップされてピックアップされてしまいます。
対策として、`BYYEARDAY=59,60`、`BYSETPOS=-1` と指定し、対象月の最大日付をピックアップします。
（開始日が 2024/2/29 でも 2025/2/28 をピックアップすることが可能）

- 開始日時: 2024/2/29
  `RRULE:FREQ=YEARLY;COUNT=5;INTERVAL=1;WKST=MO;BYMONTH=2;BYSETPOS=-1;BYYEARDAY=59,60`