Konoe Studio

PC・SW・VR・ガジェット関連ブログ

GASを使って他のGoogleカレンダーの予定を同期する

筆者の勤め先ではGoogle Workspaceのカレンダーを利用していますが、一方でプライベートでは個人のGoogleアカウントを使っています。

会社のカレンダーも「予定あり」だけの表示で個人アカウントに共有することは可能です。

しかし個人アカウントのカレンダーを他の人に公開しても、当然会社のカレンダーは共有されないため、他の人からは私の仕事の予定まではわかりません。

そこで、会社のカレンダーの予定を個人アカウントのカレンダーに同期するGASを作成したので紹介いたします。

作成にあたって、こちらのページを参考にしました。

koyacode.com

参考ページとの違い

上記ページでは ①公開用個人カレンダー ②予定追加用個人カレンダー ③会社のカレンダー からなり、公開用個人カレンダーの予定を全て削除してから、カレンダーの予定をコピーしてくるというやり方です。

しかしこのやり方では、公開用カレンダーを見た人が会議の招待を送るといったケースを想定されていません。

会議招待を受けるため、今回書いたGASのスクリプトでは同期された予定とわかる名前を自動で設定することで、同期後に会社側カレンダーでキャンセルされた予定や、新しく追加された予定であることを判断し、全消しせずとも予定を同期することができるようにしました。

スクリプト

// 会社カレンダー、同期される対象
var sourceCal = CalendarApp.getCalendarById('xxxxxxxxxxxxxxxxxxxxxxxxxxxx');
// 個人カレンダー、同期する対象
var targetCal = CalendarApp.getCalendarById('xxxxxxxxxxxxxxxxxxxxxxxxxxxx');
// 同期された予定であることを表す文字列
var syncedPrefix = '[会社] 予定';

function getEventsForDays(calendar, start_date, days)
{
  if (days == 0)
  {
    const events = calendar.getEventsForDay(start_date);
    return events;
  }
  else
  {
    const end_time = new Date(start_date.getTime() + (days * 24 * 60 * 60 * 1000));
    const events = calendar.getEvents(start_date, end_time);
    return events;
  }
}

function syncEvents(startDate, days)
{
  const sourceEvents = getEventsForDays(sourceCal, startDate, days);
  const targetEvents = getEventsForDays(targetCal, startDate, days);

  // ターゲットにあってソース側にないイベントを抽出する
  const removedEvents = [];
  targetEvents.forEach(targetValue => 
  {
    // ターゲット側でPrefixと違う場合はスキップ
    if (!targetValue.getTitle().startsWith(syncedPrefix))
    {
      return;
    }

    // ソースに開始時間と終了時間が同じものがないイベントを探す
    if(!sourceEvents.some(value => value.getStartTime().getTime() === targetValue.getStartTime().getTime() && value.getEndTime().getTime() === targetValue.getEndTime().getTime()))
    {
      removedEvents.push(targetValue);
    }
  });
  
  // 抽出したイベントをソース側から削除
  removedEvents.forEach(value => 
  {
    value.deleteEvent();
  });

  // ソース側にあってターゲット側にないイベントを抽出する
  const copyEvents = [];
  sourceEvents.forEach(sourceValue => 
  {
    // ソースに開始時間と終了時間が同じものがないイベントを探す
    if(!targetEvents.some(value => value.getTitle().startsWith(syncedPrefix) && value.getStartTime().getTime() === sourceValue.getStartTime().getTime() && value.getEndTime().getTime() === sourceValue.getEndTime().getTime()))
    {
      copyEvents.push(sourceValue);
    }
  });

  // 抽出したイベントをターゲット側に追加
  copyEvents.forEach(e => 
  {
    if (e.isAllDayEvent())
    {
      targetCal.createAllDayEvent(`${syncedPrefix}${e.getTitle()}`, e.getStartTime(), {description: e.getDescription(), location: e.getLocation()});
    }
    else
    {
      targetCal.createEvent(`${syncedPrefix}${e.getTitle()}`, e.getStartTime(), e.getEndTime(), {description: e.getDescription(), location: e.getLocation()});
    }
  });
}

function syncEventsForToday()
{
  var today = new Date();
  syncEvents(today, 1);
}

function syncEventsForTommorow()
{
  var today = new Date();
  var tomorrow = new Date(today.getTime() + (1 * 24 * 60 * 60 * 1000));
  syncEvents(tomorrow, 0);
}

function syncEventsAfter2To14Days() {
  var today = new Date();
  var two_days_after = new Date(today.getTime() + (2 * 24 * 60 * 60 * 1000));
  syncEvents(two_days_after, 14);
}

function syncEventsAfter15To60Days() {
  var today = new Date();
  var fiften_days_after = new Date(today.getTime(), (15 * 24 * 60 * 60 * 1000));
  syncEvents(fiften_days_after, 60);
}

使い方

スクリプト先頭のカレンダーを指定するIDに、カレンダーIDを設定します。

また、トリガーを設定して任意のタイミングで実行設定すると良いでしょう。

これらの設定方法は参考ページにまとまっております。