一応念のため、記録済みの予定をカレンダーで更新した場合は、既存の記録を削除して新たに追加された予定に加え更新のあった予定も最終行に追記していくスクリプトの例としました。
記録済みの予定がカレンダーからは更新されなければ、新規に追加された予定のみが追記されるます。
なお、記録済みの予定かどうかの確認のためにイベントIDと更新日を記録します。
見出し行が1行目にある前提で列見出しは、A列から
イベントID、更新日、カレンダー名、開始日、開始時刻、終了日、終了時刻
となります。
JavaScript
1//イベントIDと更新日を記録しておき新たな予定のみを追記するバージョン
2function myFunction() {
3 const numTitleRows = 1;//見出しの行数
4 const ss = SpreadsheetApp.getActiveSpreadsheet();
5 const sheetTest = ss.getSheetByName('テスト');
6 const sheetList = ss.getSheetByName('リスト');
7 const calendarIds = sheetList.getRange('C3')
8 .getValue()
9 .split(',');
10 const records = sheetTest.getDataRange().getValues();
11 records.splice(0, numTitleRows);
12 const values = [];
13 for (const calendarId of calendarIds) {
14 const calendar = CalendarApp.getCalendarById(calendarId);
15 if (!calendar) {
16 console.log(`IDが${calendarId}のカレンダーが見つかりませんでした。`);
17 continue;
18 }
19 const startTime = new Date();
20 const endTime = new Date(startTime.getTime() + 1000 * 60 * 60 * 24 * 30);
21 const events = calendar.getEvents(startTime, endTime);
22 for (const event of events) {
23 if (event.getTitle() == 'free') {
24 const eventId = event.getId();
25 const lastUpdated = event.getLastUpdated();
26 const index = records.findIndex(v => v[0] == eventId);
27 if (index > -1) {
28 if (records[index][1].getTime() == lastUpdated.getTime()) {
29 continue;//イベントIDと更新日が同じなら除外
30 } else {
31 //更新された予定の記録は一旦削除
32 sheetTest.deleteRow(index + numTitleRows + 1);
33 }
34 }
35 //新たな予定(更新も含む)を蓄積
36 values.push([
37 eventId,
38 lastUpdated,
39 calendar.getName(),
40 event.getStartTime().toLocaleDateString('ja-JP'),
41 event.getStartTime().toLocaleTimeString('ja-JP'),
42 event.getEndTime().toLocaleDateString('ja-JP'),
43 event.getEndTime().toLocaleTimeString('ja-JP'),
44 ]);
45 }
46 }
47 }
48 //最終行に追記
49 if (values.length > 0) {
50 sheetTest.getRange(sheetTest.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
51 }
52}