🎌祝日とかぶった予定を自動で削除する(Google Apps Scriptで)

# やること: 祝日とかぶった予定を自動で削除する
会議や打ち合わせの予定を繰り返しイベント(予定)で登録していませんか?私もその1人です。
この機能、便利なのですが、イベントが祝日にかぶったとき、いちいち手で消すのが面倒なのですよね…
(例えば、毎週月曜に定例の会議を入れているけど、来週は祝日だから中止だ、とかのケースです)

今回は祝日の検知と、その日のイベントを自動キャンセルするというスクリプトを実践してみましょう。

# やってみよう
手元で動かしているロジックは、もっといろんなことをやっているので、本質的な部分を切り出して紹介します。
(間違いを見つけられましたら、指摘してもらえると嬉しいです。)

**言うまでもありませんが、自己責任でお願いします。予定を削除するメソッドが含まれますので、バックアップを取るとか、削除の部分はコメントアウトして動かすなど、各自対策していただければ。**

```.js
function removeBusinessEventOnHoliday() {
  // 通知用(ここではSlackを使う)
  const slackApp = new SlackApp();

  // 評価期間(月単位)
  const from = Moment.moment().startOf("Month").toDate();
  const to = Moment.moment().add(1, "Months").startOf("Month").toDate();

  // Googleが公開している日本の祝日カレンダー
  const holidayCalendar = CalendarApp.getCalendarById("ja.japanese#holiday@group.v.calendar.google.com");
  // 自分のカレンダー
  const myCalendar = CalendarApp.getDefaultCalendar();

  // 日本の祝日カレンダーと既定のカレンダーの「特別休日」(職場の休みなど)を抽出
  [
    holidayCalendar.getEvents(from, to),
    dmyCalendar.getEvents(from, to, {
      search: "特別休日"
    })
  ].forEach(function (holidays) {
    holidays.forEach(function (holiday) {
      const holidayName = holiday.getTitle();
      const holidayFrom = holiday.getStartTime();
      const holidayTo = holiday.getEndTime();

      // 祝休日と重複する「定例」「共有会」を抽出
      ["定例", "共有会"].forEach(function (query) {
        myCalendar.getEvents(holidayFrom, holidayTo, {
          search: query
        }).forEach(function (event) {
          if (event.isRecurringEvent()) {
            const eventDate = Moment.moment(event.getStartTime());
            // 通知
            slackApp.sendText(
              eventDate.format("M/D") + "は[" + holidayName + "]のため、" +
              (event.isAllDayEvent() ? "" : eventDate.format("H:mm") + "開始の") + "[" + event.getTitle() + "]は取り消されます。"
            );
            // イベントの削除
            event.deleteEvent();
          }
        });
      });
    });
  });
}
```

# まずは評価期間内の祝日を洗い出そう
私の場合、毎月1日にこのロジックを動かすようにしています。
なので評価期間は当月初～来月初の1ヶ月間です。
日付計算は[Moment.js](https://momentjs.com/)を使っています。とても便利です。
ライブラリに組み入れて動かしてください。(参考: https://tonari-it.com/gas-moment-js-moment/)

```.js
  // 評価期間(月単位)
  const from = Moment.moment().startOf("Month").toDate();
  const to = Moment.moment().add(1, "Months").startOf("Month").toDate();
```

そして、Googleが公開している日本の祝日カレンダーと、自分のカレンダーから評価期間内の「お休み」を抽出します。
自分のカレンダーにも会社の休み(「特別休日」)が入っているので、2つのカレンダーから抽出しています。
会社が公休などをカレンダーで公開してくれているのなら、そうしたものを使いましょう。

＜ポイント＞
- 日本の祝日: Googleの日本の祝日カレンダー
- 会社の公休: 自分のカレンダーの「特別休日」というイベント

```.js
  // Googleの日本の祝日カレンダー
  const holidayCalendar = CalendarApp.getCalendarById("ja.japanese#holiday@group.v.calendar.google.com");
  // 自分のカレンダー
  const myCalendar = CalendarApp.getDefaultCalendar();

  // 日本の祝日カレンダーと既定のカレンダーの「特別休日」(会社の休みなど)を抽出
  [
    holidayCalendar.getEvents(from, to),
    myCalendar.getEvents(from, to, {
      search: "特別休日"
    })
  ].forEach(function (holidays) {
     // 後述
  });
}
```
# 祝日に行われているイベントを抽出しよう
祝日が取得できたら、祝日の開始～終了までに行われているイベントを抽出しましょう。

ただし、いくつか前提を設けています。
1. 消す対象は「定例」と「共有会」に限定しました。(お好みで)
2. 消す対象は「繰り返しイベント」に限定しています。
3. 祝日をまたぐようなイベントは想定しない。(冬期休暇とか)

```.js
      // 祝休日と重複する「定例」「共有会」を抽出
      ["定例", "共有会"].forEach(function (query) {
        defaultCalendar.getEvents(holidayFrom, holidayTo, {
          search: query
        }).forEach(function (event) {
          if (event.isRecurringEvent()) {
            // 通知…ここでは割愛

            // イベントの削除
            event.deleteEvent();
          }
        });
      });
    });
```
# 最後に

最後まで読んでくださってありがとうございます。
今回はちょっと簡単過ぎでしたでしょうか:sweat:

最初、祝日テーブルをスプレッドシートに置こうとしていたのですが、祝日のメンテナンスをしたくないので、Googleカレンダーを使わせてもらいました。
日本は祝日が多い国ですが、特にスクリプトが重いということはなかったです。月に1～2日程度ですからね。

それでは。