Google Apps Scriptの勉強!
Bell
CAEエンジニアのデジタルキャンバス
Googleスプレッドシートに記載した複数の予定をGoogleカレンダーに一括登録することができました!
ここでは運用寄りのワークフローを紹介します。
スプレッドシートを公開しているので、ぜひ最後までご覧ください
こちらをコピーしてお使いください。(閲覧→自分のドライブにコピー)
https://docs.google.com/spreadsheets/d/1WEH4Ef8WNJL1y4S6a8GiH7iuVx_lTo12Fuc0TgQnS7o/edit?usp=sharing
ヘッダー(1行目・固定)
・件名[必須]:予定のタイトル
・日付[必須]:2025/09/01 など
・開始/終了:09:30 など(終了が空欄なら60分)
・終日:TRUEで開始/終了の時間列を無視
・説明:予定の詳細を記載
・場所:予定の場所を記載
・結果:実行ログが入ります
最小例
通常:件名=打合せ / 日付=2025/09/01 / 開始=09:30 / 終了=10:30
終日:件名=出張 / 日付=2025/09/02 / 終日=TRUE
ワンクリックで簡単♪
結果列に作成:<イベントID>が入ります。
Q. primary以外のカレンダーへ作りたい
A. この“超シンプル版”は未対応です。CalendarApp.getCalendarById()
を1行追加する拡張例を記事末に記載します。
Q. 更新や削除は?
A. 目的外のため未対応。拡張版(CREATE/UPDATE/DELETE)へ切り替えてください。
Q. 終了未指定の所要時間を変えたい
A. コード中の +60*60000
を希望の分に変更(例:90分なら +90*60000
)。
/**
* 超シンプル:スプレッドシートの内容を Googleカレンダー(primary)へ一括【作成のみ】
* 必要な列(1行目のヘッダー名は固定):
* 件名 / 日付 / 開始 / 終了 / 終日 / 説明 / 場所 / 結果
* - 结束が空なら既定60分。
* - 終日=TRUE なら時間列は無視(複数日対応したい場合はシート側で日付を分けてください)。
*/
const H = { title:'件名', date:'日付', start:'開始', end:'終了', allDay:'終日', desc:'説明', loc:'場所', out:'結果' };
function onOpen(){ SpreadsheetApp.getUi().createMenu('カレンダー登録').addItem('一括作成','bulkCreate').addToUi(); }
function bulkCreate(){
const sh = SpreadsheetApp.getActiveSheet();
const rows = sh.getDataRange().getValues();
if(rows.length<2) return;
const idx = colMap(rows[0]);
const cal = CalendarApp.getDefaultCalendar();
for(let r=1;r<rows.length;r++){
try{
const row = rows[r];
const title = cell(row,idx,H.title); if(!title) continue;
const d = asDate(cell(row,idx,H.date)); if(!d){ write(sh,idx,r,'日付が不正'); continue; }
const allDay = asBool(cell(row,idx,H.allDay));
const desc = cell(row,idx,H.desc)||''; const loc = cell(row,idx,H.loc)||'';
if(allDay){
const ev = cal.createAllDayEvent(title, strip(d), {description:desc, location:loc});
write(sh,idx,r,'作成: '+ev.getId());
}else{
const [shh,shm] = asHM(cell(row,idx,H.start), 9, 0);
const start = new Date(d.getFullYear(),d.getMonth(),d.getDate(),shh,shm);
let end;
const endCell = cell(row,idx,H.end);
if(endCell){ const [eh,em] = asHM(endCell, shh, shm); end = new Date(d.getFullYear(),d.getMonth(),d.getDate(),eh,em); }
else { end = new Date(start.getTime()+60*60000); }
if(end<=start){ write(sh,idx,r,'終了が開始以下'); continue; }
const ev = cal.createEvent(title,start,end,{description:desc,location:loc});
write(sh,idx,r,'作成: '+ev.getId());
}
}catch(e){ write(sh,idx,r,'エラー: '+(e&&e.message||e)); }
}
}
// --- helpers (短いままに) ---
function colMap(header){ const m={}; header.forEach((h,i)=>m[h]=i); return m; }
function cell(row,idx,name){ const i=idx[name]; return i==null?'':row[i]; }
function write(sh,idx,r,msg){ const i=idx[H.out]; if(i==null) return; sh.getRange(r+1,i+1).setValue(msg); }
function asBool(v){ if(typeof v==='boolean') return v; const s=String(v||'').toLowerCase(); return ['true','t','yes','y','1','on','○','◯'].includes(s); }
function asDate(v){ if(v instanceof Date) return v; const d=new Date(v); return isNaN(d)?null:d; }
function asHM(v,defH,defM){ if(v instanceof Date) return [v.getHours(),v.getMinutes()]; const m=String(v||'').match(/^(\d{1,2}):(\d{2})$/); return m?[+m[1],+m[2]]:[defH,defM]; }
function strip(d){ return new Date(d.getFullYear(),d.getMonth(),d.getDate()); }