複数所属のカレンダー同期に疲れた人へ

## 動機

複数の団体に所属してると、スケジュールの同期に四苦八苦することになります。ダブルブッキングにならないよう、既に予定が埋まってることを示す "用事あり" を、双方のカレンダーに登録する作業がとても大変でした。A 社の予定を B 社のカレンダーにコピーし、B 社の予定を A 社にコピーし…。

そんな中、 GAS (Google Apps Script) を使うというアイデアをもらったので、作ってみました。Google Calendar 同士であれば、これでカレンダーの同期作業が大幅に軽減されると思います。

## 想定運用

各所属のメインとなるカレンダーとは別に、同期用のカレンダーを作成します。

メインとなるカレンダーは、その所属先の予定と、既にブッキングされていることを表明するための "用事あり" を入れます。

同期用のカレンダーは、メインカレンダーから "用事あり" を除外した予定をコピーします。このカレンダーを、もう片方の所属先の同期に使います。

## GAS コード

**想定運用** の二つのカレンダーの同期を取ります。次のコードは、メインとなるカレンダーから、同期用カレンダーに予定をコピーするコードです。

ご自身の GAS 環境に入れる場合は、以下のコードの中に二つのカレンダーの ID を設定することと、トリガーを設定することが必要になります。

- [GoogleカレンダーIDの探し方と公開設定](https://feed.mikle.com/ja/support/calender-id/)
- [Google Apps Scriptのトリガーについて](https://blog.synnex.co.jp/google/gas-trigger/)

```js
const sourceCalendarID = '' // メインとなるカレンダーの ID
const targetCalendarID = '' // 同期用カレンダーの　ID

/**
 * 一週間分のイベントを同期する
 * カレンダーの変更をトリガーにする想定
 */
function updateOneWeek() {
  const fromDateTime = new Date()
  const toDateTime = new Date()
  toDateTime.setDate(fromDateTime.getDate() + 7)

  libUpdateCalendar(fromDateTime, toDateTime)
}

/**
 * 三ヶ月分のイベントを同期する
 * 一日に一度、夜中に起動する、ぐらいの想定
 */
function updateThreeMonths() {
  const fromDateTime = new Date()
  const toDateTime = new Date()
  toDateTime.setDate(fromDateTime.getDate() + 90)

  libUpdateCalendar(fromDateTime, toDateTime)
}

/**
 * 指定範囲のカレンダーを同期する
 * （Utility)
 */
const libUpdateCalendar = (fromDateTime, toDateTime) => {
  // ターゲットカレンダーから、指定期間のイベントを削除
  const targetCalendar = CalendarApp.getCalendarById(targetCalendarID)
  const targetEvents = targetCalendar.getEvents(fromDateTime, toDateTime)
  targetEvents.forEach(it => it.deleteEvent())

  // 指定期間のイベントをソースカレンダーからターゲットカレンダーへ複製
  const sourceCalendar = CalendarApp.getCalendarById(sourceCalendarID)
  const sourceEvents = sourceCalendar.getEvents(fromDateTime, toDateTime)

  sourceEvents.forEach(it => {
    if (it.getTitle() === '用事あり') return
    if (it.getMyStatus() === CalendarApp.GuestStatus.NO) return
    if (it.isAllDayEvent()) return

    targetCalendar.createEvent(it.getTitle(), it.getStartTime(), it.getEndTime())
  })
}

```

こちらは、自動的に "用事あり" を作るコードです。

```js
const mainCalendarID = ''     // メインとなるカレンダーの ID
const otherCalendarIDs = [''] // ブロックするイベントのあるカレンダーの ID　(複数可)

/**
 * 一週間分のイベントを同期する
 * 一時間ごとぐらいの想定
 */
function updateOneWeek() {
  const fromDateTime = new Date()
  const toDateTime = new Date()
  toDateTime.setDate(fromDateTime.getDate() + 7)

  libUpdateCalendar(fromDateTime, toDateTime)
}

/**
 * 三ヶ月分のイベントを同期する
 * 一日に一度、夜中に起動する、ぐらいの想定
 */
function updateThreeMonthes() {
  const fromDateTime = new Date()
  const toDateTime = new Date()
  toDateTime.setDate(fromDateTime.getDate() + 90)

  libUpdateCalendar(fromDateTime, toDateTime)
}

/**
 * 指定範囲のカレンダーを同期する
 * （Utility)
 */
const libUpdateCalendar = (fromDateTime, toDateTime) => {
  // メインカレンダーから、指定期間のブロックイベントを削除
  const mainCalendar = CalendarApp.getCalendarById(mainCalendarID)
  const targetEvents = mainCalendar.getEvents(fromDateTime, toDateTime)
  targetEvents.forEach(it => {
    if (it.getTitle() !== '用事あり') return
    it.deleteEvent()
  })

  // 指定期間のイベントについて、メインカレンダーにブロックイベントを作成
  const blocks = otherCalendarIDs.map(it =>
    CalendarApp.getCalendarById(it).getEvents(fromDateTime, toDateTime)
  )

  blocks.flat().forEach(it =>
    mainCalendar.createEvent('用事あり', it.getStartTime(), it.getEndTime())
  )
}
```