GASでスプレッドシートの予定を
Googleカレンダーに一括登録する方法
スプレッドシートに並べた予定を、CalendarApp(GASでGoogleカレンダーを操作するクラス)でまとめてカレンダーに登録する方法を、動くコードで解説します。二重登録の防止や終日予定の扱いまで紹介します。
Table of Contents
どんな場面で使えるか
予定を1件ずつカレンダーに手入力するのは手間がかかります。GASを使えば、スプレッドシートで一覧管理している予定を、まとめてGoogleカレンダーに反映できます。次のような場面で役立ちます。
シフト・勤務表の反映
スプレッドシートで作ったシフト表を、担当者のカレンダーへ一括で登録する
イベント・研修日程
セミナーや研修の日程一覧を、まとめて共有カレンダーに反映する
定期タスクの予定化
月次の締め日や請求日など、繰り返し発生する予定を一括で登録する
予約・アポの取り込み
フォームやAPIで集めた予約データを、営業カレンダーへ自動で反映する
スプレッドシートの列構成を決める
まずは予定を管理する列を決めます。最小構成なら「タイトル」「開始日時」「終了日時」の3列です。二重登録を防ぐために、登録済みかどうかを記録する「イベントID」列も用意しておくのがおすすめです。
| 列 | 内容 | 例 |
|---|---|---|
| A | 予定のタイトル | 定例ミーティング |
| B | 開始日時(日付型セル) | 2026/07/20 10:00 |
| C | 終了日時(日付型セル) | 2026/07/20 11:00 |
| D | 場所・ゲストなど(任意) | 会議室A |
| E | イベントID(登録後に自動記入) | (空欄でOK) |
B列・C列は、セルの書式を「日時」にしておくと、GAS側でそのままDate(日時を表すオブジェクト)として取得でき、変換の手間が減ります。
基本:1行ずつカレンダーに登録する
まずは最小構成のコードです。getDataRange().getValues()でシート全体を2次元配列として一括取得し、ヘッダー行を除いた各行をcreateEvent()で登録します。
// A列:タイトル B列:開始日時 C列:終了日時 を想定
function registerEventsBasic() {
const sheet = SpreadsheetApp.getActive().getSheetByName("予定");
const calendar = CalendarApp.getDefaultCalendar();
const data = sheet.getDataRange().getValues();
const rows = data.slice(1); // ヘッダー行を除外
rows.forEach((row) => {
const title = row[0];
const start = row[1]; // 日付型セルはDateとして取得される
const end = row[2];
if (!title || !start || !end) return;
calendar.createEvent(title, start, end);
});
}登録先を共有カレンダーにしたい場合は、getDefaultCalendar()をgetCalendarById("カレンダーID")に差し替えます。カレンダーIDはGoogleカレンダーの「設定と共有」画面で確認できます。
日付と時刻をDateに組み立てる
日付と時刻が別々の列に入っている場合は、new Date()で1つの日時に組み立てます。ここで注意したいのが「月は0始まり」という点です。7月は6で表します。日付セルからgetMonth()で取り出せば、この0始まりの値がそのまま返るため、ずれずに扱えます。
// 日付と時刻が別の列に分かれている場合の組み立て
// A列:タイトル B列:日付(2026/07/20) C列:開始時刻(10:00) D列:終了時刻(11:30)
function buildDate(dateValue, timeText) {
const d = new Date(dateValue);
const [hour, minute] = String(timeText).split(":").map(Number);
// 月は0始まりではなく、既存のDateから年月日を引き継ぐ
return new Date(
d.getFullYear(),
d.getMonth(),
d.getDate(),
hour || 0,
minute || 0
);
}イベントIDで二重登録を防ぐ
スクリプトを再実行すると、同じ予定が何度も登録されてしまいます。これを防ぐには、登録に成功した行にevent.getId()を書き戻し、そのIDが空の行だけを処理対象にします。
// E列に登録済みイベントIDを書き戻して二重登録を防ぐ
function registerEventsOnce() {
const sheet = SpreadsheetApp.getActive().getSheetByName("予定");
const calendar = CalendarApp.getDefaultCalendar();
const data = sheet.getDataRange().getValues();
const header = data[0];
const idColIndex = header.indexOf("イベントID"); // E列など
for (let i = 1; i < data.length; i++) {
const row = data[i];
const title = row[0];
const start = row[1];
const end = row[2];
const alreadyRegistered = row[idColIndex];
// 未登録かつ必要な値がそろっている行だけ処理
if (alreadyRegistered || !title || !start || !end) continue;
const event = calendar.createEvent(title, start, end);
// getId()を書き戻す(1始まりなので行番号は i + 1、列も +1)
sheet.getRange(i + 1, idColIndex + 1).setValue(event.getId());
}
}この仕組みなら、予定を追記して再実行しても、新しく追加した行だけが登録されます。書き戻したイベントIDは、後から予定を更新・削除する際にも使えます。
説明・場所・ゲストを設定する
createEvent()の第4引数にオプションを渡すと、場所・説明・ゲスト(招待者)を一緒に設定できます。sendInvitesをfalseにすれば、招待メールを送らずに予定だけを追加できます。
// 説明・場所・ゲストを付けて登録する
function registerEventsWithOptions() {
const sheet = SpreadsheetApp.getActive().getSheetByName("予定");
const calendar = CalendarApp.getDefaultCalendar();
const rows = sheet.getDataRange().getValues().slice(1);
rows.forEach((row) => {
const [title, start, end, location, guests, description] = row;
if (!title || !start || !end) return;
calendar.createEvent(title, start, end, {
location: location || "",
description: description || "",
// カンマ区切りのメールアドレスをゲストに追加
guests: guests || "",
sendInvites: false, // 招待メールを送らない場合はfalse
});
});
}ゲストはカンマ区切りのメールアドレス文字列で指定します。招待メールを送る運用にする場合は、誤送信を防ぐため、テスト時は必ず自分宛のアドレスで確認してください。
終日予定と時間指定を切り替える
時間を持たない終日予定にはcreateAllDayEvent()を使います。開始時刻のセルが空なら終日、値があれば時間指定として振り分ければ、1つのループで両方に対応できます。
// 終日予定と時間指定を1つのループで切り替える
// E列が空なら終日、時刻が入っていれば時間指定として扱う
function registerMixedEvents() {
const sheet = SpreadsheetApp.getActive().getSheetByName("予定");
const calendar = CalendarApp.getDefaultCalendar();
const rows = sheet.getDataRange().getValues().slice(1);
rows.forEach((row) => {
const title = row[0];
const dateValue = row[1]; // 日付
const startTime = row[2]; // 開始時刻(空なら終日)
const endTime = row[3];
if (!title || !dateValue) return;
if (!startTime) {
// 終日予定
calendar.createAllDayEvent(title, new Date(dateValue));
} else {
const start = buildDate(dateValue, startTime);
const end = buildDate(dateValue, endTime || startTime);
calendar.createEvent(title, start, end);
}
});
}トリガーで自動化する・実務の注意点
フォームやAPI連携で予定が増え続けるシートには、時間主導トリガーを設定して、未登録の予定を自動でカレンダーへ反映するのが効果的です。
// 毎朝7時に未登録の予定をカレンダーへ反映する
function createDailyTrigger() {
ScriptApp.newTrigger("registerEventsOnce")
.timeBased()
.everyDays(1)
.atHour(7)
.create();
}1. タイムゾーンを合わせる
スクリプトのタイムゾーンがカレンダーとずれていると、登録時刻がずれます。スクリプトエディタの「プロジェクトの設定」で、タイムゾーンを「Asia/Tokyo」に設定しておきましょう。
2. 大量登録は実行時間制限に注意
createEventはカレンダーへのAPI呼び出しを伴うため、数百件規模になるとGASの6分制限に達することがあります。イベントID列で登録済みをスキップする仕組みにしておけば、複数回に分けて実行しても重複しません。
3. 初回実行時は承認が必要
CalendarAppを初めて使うときは、カレンダーへのアクセス承認を求められます。画面の指示に従って許可すれば、以降はトリガーからも自動で実行できます。
まとめ
GASでのカレンダー一括登録は、CalendarAppのcreateEventとスプレッドシートのループを組み合わせるだけで実装できます。日付はDateで組み立て、イベントIDの書き戻しで二重登録を防ぎ、終日予定はcreateAllDayEventで使い分けるのがポイントです。定期トリガーと組み合わせれば、予定の管理をスプレッドシート側に一本化しつつ、カレンダーへの反映を自動化できます。
よくある質問
できます。CalendarApp.getDefaultCalendar()でカレンダーを取得し、createEvent(タイトル, 開始日時, 終了日時)で予定を作成します。スプレッドシートの各行をループで回し、1行ずつcreateEventを呼べば、まとめて一括登録できます。
CalendarApp.getCalendarById(カレンダーID)を使います。カレンダーIDはGoogleカレンダーの「設定と共有」画面で確認でき、多くの場合はメールアドレス形式(例: xxxx@group.calendar.google.com)です。デフォルトカレンダーのgetDefaultCalendar()と差し替えるだけで登録先を変えられます。
登録に成功したらcreateEventが返すイベントのgetId()をスプレッドシートの列に書き戻し、その列が空の行だけを処理対象にします。こうすると、スクリプトを再実行しても登録済みの行はスキップされ、二重登録を防げます。
GASのcreateEventはJavaScriptのDateオブジェクトを受け取ります。セルが日付型ならgetValues()で自動的にDateとして取得できます。日付と時刻が別のセルに分かれている場合は、new Date(年, 月-1, 日, 時, 分)で組み立てます。月は0始まりなので、1を引く点に注意してください。
できます。時間を持たない終日予定にはcreateAllDayEvent(タイトル, 日付)を使います。開始日と終了日を指定する複数日にまたがる終日予定にも対応しています。時間指定の予定はcreateEvent、終日予定はcreateAllDayEventと使い分けます。
できます。時間主導トリガー(ScriptApp.newTrigger)で、毎朝などの決まったタイミングに登録関数を実行するよう設定できます。フォームやAPI連携で継続的に予定行が追加されるシートと組み合わせると、手作業なしでカレンダーへ反映できます。
GAS開発・業務Webシステムを
相談する。
カレンダー連携以外にも、フォーム連携・帳票出力・Gmail自動化など、日々の業務を自動化するGAS開発をご相談いただけます。