GASでGoogleカレンダーの
予定を取得・集計する方法
CalendarApp(GoogleカレンダーをGASから扱うサービス)で、予定を期間指定で取得し、スプレッドシートへの書き出しやタイトル別の稼働時間集計、毎朝の自動出力までを動くコードで解説します。
Table of Contents
getEventsで期間を指定して予定を取得する
基本はgetEvents(開始日, 終了日)です。引数はどちらもDate型で、開始日以上・終了日未満の範囲にかかる予定がCalendarEventの配列で返ります。終了日はその日を含めたい場合、翌日の0時を渡すのが安全です。
function logEventsThisWeek() {
const calendar = CalendarApp.getDefaultCalendar();
// 今日の0時から7日後の0時まで(終了日は含めたい範囲の「翌日」を渡す)
const start = new Date();
start.setHours(0, 0, 0, 0);
const end = new Date(start);
end.setDate(end.getDate() + 7);
const events = calendar.getEvents(start, end);
Logger.log("取得件数: " + events.length);
events.forEach((event) => {
Logger.log(event.getTitle() + " / " + event.getStartTime());
});
}getDefaultCalendar()はログインユーザー自身のメインカレンダーを指します。初回実行時はカレンダーへのアクセス許可を求められるので、承認してください。
予定をスプレッドシートに書き出す
取得した予定を1行1予定の二次元配列に変換し、setValues()でまとめて書き出します。日時はUtilities.formatDateで読みやすい書式に整えます。
function exportEventsToSheet() {
const calendar = CalendarApp.getDefaultCalendar();
const tz = Session.getScriptTimeZone(); // 例: "Asia/Tokyo"
const start = new Date();
start.setHours(0, 0, 0, 0);
const end = new Date(start);
end.setDate(end.getDate() + 7);
const events = calendar.getEvents(start, end);
// 予定を1行=1予定の二次元配列に変換
const rows = events.map((event) => [
Utilities.formatDate(event.getStartTime(), tz, "yyyy/MM/dd HH:mm"),
Utilities.formatDate(event.getEndTime(), tz, "yyyy/MM/dd HH:mm"),
event.getTitle(),
event.getLocation() || "",
event.getGuestList().map((g) => g.getEmail()).join(", "),
event.isAllDayEvent() ? "終日" : "時間指定",
]);
const sheet = SpreadsheetApp.getActive().getSheetByName("予定一覧");
sheet.clearContents();
const header = ["開始", "終了", "件名", "場所", "参加者", "種別"];
sheet.getRange(1, 1, 1, header.length).setValues([header]);
if (rows.length > 0) {
sheet.getRange(2, 1, rows.length, header.length).setValues(rows);
}
}getGuestList()は参加者オブジェクトの配列を返すため、getEmail()でメールアドレスを取り出してから連結しています。1件ずつsetValueせず、配列にまとめて一括書き込みするのが高速化のコツです。
getEventsForDayで当日分だけ取得する
「今日の予定だけ欲しい」というときは、開始・終了日を計算しなくてもgetEventsForDay(Date)が使えます。渡したDateの1日分の予定が返ります。
function exportTodayEvents() {
const calendar = CalendarApp.getDefaultCalendar();
const tz = Session.getScriptTimeZone();
// 当日分だけを取得(開始・終了日の計算が不要)
const events = calendar.getEventsForDay(new Date());
const rows = events.map((event) => [
Utilities.formatDate(event.getStartTime(), tz, "HH:mm"),
Utilities.formatDate(event.getEndTime(), tz, "HH:mm"),
event.getTitle(),
]);
const sheet = SpreadsheetApp.getActive().getSheetByName("本日の予定");
sheet.clearContents();
sheet.getRange(1, 1, 1, 3).setValues([["開始", "終了", "件名"]]);
if (rows.length > 0) {
sheet.getRange(2, 1, rows.length, 3).setValues(rows);
}
}特定のキーワードを含む予定だけ扱いたい場合は、events.filter((e) => e.getTitle().includes("会議"))のようにJavaScript側で絞り込みます。
タイトル別に稼働時間を集計する
予定取得の実用例が「どの業務に何時間使ったか」の集計です。 各予定のgetEndTime()とgetStartTime()の差からミリ秒単位の所要時間を出し、タイトルごとに合計します。
function aggregateHoursByTitle() {
const calendar = CalendarApp.getDefaultCalendar();
// 今月1日から翌月1日まで
const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), 1);
const end = new Date(now.getFullYear(), now.getMonth() + 1, 1);
const events = calendar.getEvents(start, end);
const totals = {}; // { 件名: 合計時間(時間) }
events.forEach((event) => {
if (event.isAllDayEvent()) return; // 終日予定は集計から除外
const ms = event.getEndTime().getTime() - event.getStartTime().getTime();
const hours = ms / (1000 * 60 * 60);
const title = event.getTitle();
totals[title] = (totals[title] || 0) + hours;
});
// オブジェクト → 二次元配列(時間の多い順に並べ替え)
const rows = Object.keys(totals)
.map((title) => [title, Math.round(totals[title] * 10) / 10])
.sort((a, b) => b[1] - a[1]);
const sheet = SpreadsheetApp.getActive().getSheetByName("稼働集計");
sheet.clearContents();
sheet.getRange(1, 1, 1, 2).setValues([["件名", "合計時間"]]);
if (rows.length > 0) {
sheet.getRange(2, 1, rows.length, 2).setValues(rows);
}
}終日予定はisAllDayEvent()で除外しています。含めてしまうと1件あたり24時間として計上され、稼働時間が実態から大きくずれるためです。
共有カレンダーの予定を取得する
自分以外の共有カレンダーを扱うときは、getCalendarById(カレンダーID)でカレンダーを取得します。カレンダーIDはGoogleカレンダーの「設定と共有 › カレンダーの統合」で確認でき、閲覧権限が共有されている必要があります。
function exportSharedCalendar() {
// カレンダーIDはGoogleカレンダーの「設定と共有 > カレンダーの統合」で確認できる
const calendarId = "team@example.com";
const calendar = CalendarApp.getCalendarById(calendarId);
if (!calendar) {
throw new Error("カレンダーが見つかりません。IDと共有権限を確認してください。");
}
const start = new Date();
start.setHours(0, 0, 0, 0);
const end = new Date(start);
end.setDate(end.getDate() + 1);
const events = calendar.getEvents(start, end);
Logger.log(calendar.getName() + " の本日の予定: " + events.length + "件");
}権限がない、またはIDが誤っているとgetCalendarByIdがnullを返します。そのままgetEventsを呼ぶとエラーになるため、上記のようにnullチェックを入れておくと原因がわかりやすくなります。
毎朝の自動出力をトリガーで設定する
当日の予定書き出しは、時間主導トリガーで毎朝自動実行すると便利です。トリガーはコードから作成でき、二重登録を防ぐため既存トリガーを削除してから登録します。
function createMorningTrigger() {
// 二重登録を防ぐため、同名の既存トリガーを削除してから作成
ScriptApp.getProjectTriggers().forEach((t) => {
if (t.getHandlerFunction() === "exportTodayEvents") {
ScriptApp.deleteTrigger(t);
}
});
ScriptApp.newTrigger("exportTodayEvents")
.timeBased()
.everyDays(1)
.atHour(7)
.create();
}このcreateMorningTriggerを一度だけ手動実行すれば、以後は毎朝7時台にexportTodayEventsが動きます。書き出したシートをそのままGmailで送れば、当日の予定通知メールにもなります。
実務での注意点
1. タイムゾーンを必ず意識する
日時を文字列にするときはUtilities.formatDateにタイムゾーンを渡します。省略や誤指定で9時間ずれることがあるため、Session.getScriptTimeZone()やAsia/Tokyoを明示するのが安全です。
2. 取得件数と期間の広げすぎに注意
数年分など極端に広い期間を一度に取得すると、件数が多く処理が重くなります。必要な期間に絞り、大量集計は月単位に分けて実行すると安定します。
3. 定期予定(繰り返し)は展開後の1件ずつで返る
毎週の定例会などの繰り返し予定は、期間内で発生する回数分だけ個別のCalendarEventとして返ります。元の繰り返しルールをまとめて扱いたい場合はgetEventSeries()を使います。
まとめ
GASでのカレンダー予定取得は、getEventsとgetEventsForDayの使い分けが基本です。取得した予定をスプレッドシートに書き出し、タイトル別に集計すれば、稼働時間の見える化や日次の予定通知を自動化できます。共有カレンダーの参照と定期トリガーを組み合わせれば、チーム全体の予定管理にも応用できます。
よくある質問
CalendarApp.getDefaultCalendar().getEvents(開始日, 終了日) を使います。引数はDate型で、開始日以上・終了日未満の範囲にかかる予定がCalendarEventの配列で返ります。終了日はその日を含めたいなら翌日の0時を渡すのが安全です。
できます。CalendarApp.getCalendarById(カレンダーID) でカレンダーを取得してから getEvents を呼びます。カレンダーIDはGoogleカレンダーの設定画面の「カレンダーの統合」から確認でき、閲覧権限が共有されている必要があります。
CalendarEventの各メソッドで取り出します。getTitle()でタイトル、getStartTime()・getEndTime()で開始・終了のDate、getLocation()で場所、getGuestList()で参加者の配列が取れます。参加者のメールはgetGuestList()の各要素のgetEmail()で取得します。
CalendarEventのisAllDayEvent()がtrueなら終日予定です。終日予定はgetEndTime()が翌日の0時を指すため、そのまま所要時間を計算すると24時間になります。稼働時間の集計では終日予定を除外するか別枠で扱うのが安全です。
できます。getEventsForDay(new Date()) で当日分を取得する関数を作り、ScriptApp.newTriggerの時間主導トリガーで毎朝実行するよう設定します。前日分の日報用サマリーや、当日の予定一覧の通知メールと組み合わせる使い方が定番です。
GAS開発・業務Webシステムを
相談する。
カレンダー連携以外にも、予定の集計レポート・帳票出力・Gmail自動通知など、日々の業務を自動化するGAS開発をご相談いただけます。