``` function syncExpiryEventsToCalendar() { var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); var data = sheet.getDataRange().getValues(); // ✅ 指定日曆(建議) var calendar = CalendarApp.getCalendarById("YourID"); // 若要用預設日曆,改為:var calendar = CalendarApp.getDefaultCalendar(); // 欄位對應(含 Timestamp) // 0: Timestamp(不用) // 1: 品名 // 2: 保存期限 // 3: 提前提醒天數 // 4: 備註 // 5: 事件ID(程式自動填) // 1) 收集目前表內的事件ID var sheetEventIds = {}; for (var i = 1; i < data.length; i++) { var idInSheet = data[i][5]; if (idInSheet) sheetEventIds[idInSheet] = true; } // 2) 新增或更新事件 for (var i = 1; i < data.length; i++) { var foodName = data[i][1]; var rawDate = data[i][2]; var remindDays = parseInt(data[i][3], 10) || 0; var note = data[i][4] || ""; var eventId = data[i][5]; if (!foodName || !rawDate) continue; // 轉成 Date,並鎖在當地午夜,避免時區偏移 var d = new Date(rawDate); if (isNaN(d)) continue; var expiryDate = new Date(d.getFullYear(), d.getMonth(), d.getDate()); var eventTitle = foodName + "到期"; var startTime = expiryDate; var endTime = new Date(expiryDate.getTime() + 60 * 60 * 1000); // 1小時事件 var event = null; // 先用事件ID找舊事件 if (eventId) { try { event = calendar.getEventById(eventId); } catch (e) { event = null; } } // 若找到 → 不管改了哪個欄位,都直接更新事件 if (event) { // 更新標題、描述、時間 event.setTitle(eventTitle); event.setDescription(note); event.setTime(startTime, endTime); // 先清空提醒,再依「提前提醒天數」重設 event.removeAllReminders(); if (remindDays > 0) { event.addPopupReminder(remindDays * 24 * 60); // 分鐘 } // 標記此事件仍存在於表內,供後面清理用 sheetEventIds[event.getId()] = true; continue; // 已更新完畢,跳過「新建」邏輯 } // 不存在就新建 if (!event) { var newEvent = calendar.createEvent(eventTitle, startTime, endTime, { description: note }); if (remindDays > 0) newEvent.addPopupReminder(remindDays * 24 * 60); // 寫回事件ID到第 6 欄 sheet.getRange(i + 1, 6).setValue(newEvent.getId()); sheetEventIds[newEvent.getId()] = true; } } // 3) 清理:刪掉已從表中移除的事件(只掃未來90天,且標題以「到期」結尾的) var today = new Date(); var future = new Date(); future.setDate(today.getDate()+360); var allEvents = calendar.getEvents(today, future); for (var j = 0; j < allEvents.length; j++) { var ev = allEvents[j]; var evId = ev.getId(); if (ev.getTitle().endsWith("到期") && !sheetEventIds[evId]) { ev.deleteEvent(); } } } ```