Google 日曆: 取得預設日曆 getDefaultCalendar()

 


如果你常把表單或試算表資料匯進 Google 日曆,卻老是卡在「calendarId 要填哪個?」那多半是走錯門。

對 Apps Script 來說,CalendarApp.getDefaultCalendar() 直接把主要行事曆交到你手上,少了半套設定、多了不少穩定。本文不賣關子,先給可跑的範例,再拆小步:如何新增活動、查重避免洗版、加提醒、改顏色、配合觸發器排程;最後附一張避雷清單,像時區偏移、Email 不等於 Calendar ID、事件 ID 混用等地雷。輕鬆上手,但細節一個都不漏。希望本篇文章能夠幫助到需要的您。


目錄

{tocify} $title={目錄} 


為什麼要懂 getDefaultCalendar()?


如果你用 Google Apps Script 幫日常工作自動化(排程會議、建立提醒、從表單或試算表自動寫入活動),你一定會碰到「我要把活動寫到哪一個行事曆?」這題。最直覺的答案就是:寫到自己帳號的主要行事曆(Primary)。在 Apps Script 裡,這一行就到位了:

const cal = CalendarApp.getDefaultCalendar();

拿到 cal 之後,你可以查活動、建立活動、刪除活動、設定顏色、加提醒…幾乎一條龍處理。Google 官方文件說得很白,CalendarApp.getDefaultCalendar() 會直接回傳目前執行腳本的使用者的預設(主要)行事曆物件。

另外,如果你是走「Calendar API」而不是 Apps Script 的內建服務,對應的概念是把 calendarId 指成 "primary" 就能指到使用者主要行事曆。


快速理解:getDefaultCalendar() 跟其他取得方式差在哪?

getDefaultCalendar():回傳目前使用者帳號的主要行事曆(最常用)。

getCalendarById(id):用行事曆 ID 直接抓(含共同/共享行事曆)。

getCalendarsByName(name):用名稱粗抓,但名稱可重複,不保證唯一。

(Calendar API)calendarId="primary":API 世界裡的「主要行事曆」寫法。


小提醒:很多人以為「我的 Gmail 地址」就是 Calendar ID。不是。Apps Script 用 getDefaultCalendar() 省去查 ID 的麻煩;如果走 API,請用 "primary" 或去後台設定頁查真正的 Calendar ID。


實作前準備(零基礎也能上手)

1.    打開 Apps Script 編輯器

在雲端硬碟按「新增 → Google Apps Script」,或在試算表/文件裡「擴充功能 → App Script」。

2.    選擇執行帳戶並授權

第一次執行會跳授權視窗,因為你要操作日曆事件,屬於敏感權限,照步驟允許即可。

3.    規劃時區

到專案設定把時區改成你的所在時區,避免日期錯位(台灣常見 +08:00)。


最小可用範例(CRUD 一次看懂)

下面這組範例把最常用的「查、增、改、刪」打包。你可以一段一段貼去跑。

1.    建立一般活動

function createEvent() {
  const cal = CalendarApp.getDefaultCalendar();
  const start = new Date('2025-10-12T14:00:00+08:00');
  const end   = new Date('2025-10-12T15:00:00+08:00');
  const event = cal.createEvent('行銷例會', start, end, {
    description: '每週例會,準備 KPI',
    location: '會議室 A',
    guests: 'teammate1@company.com, teammate2@company.com',
    sendInvites: true
  });
  Logger.log('Created: ' + event.getId());
}


2.    建立整天活動(避免跨日坑)

function createAllDayEvent() {
  const cal = CalendarApp.getDefaultCalendar();
  // 整天活動用 new Date(年, 月(0-11), 日) 會更直覺
  const event = cal.createAllDayEvent('團建日', new Date(2025, 9, 25)); // 2025-10-25
  Logger.log('All-day: ' + event.getId());
}


3.    查詢某段期間活動(用於重複寫入前先查重)

function listEventsThisWeek() {
  const cal = CalendarApp.getDefaultCalendar();
  const now = new Date();
  const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const end = new Date(start); end.setDate(end.getDate() + 7);
  const events = cal.getEvents(start, end);
  events.forEach(e => Logger.log([e.getTitle(), e.getStartTime(), e.getId()].join(' | ')));
}


4.    依條件找活動並更新

function updateEventByTitle() {
  const cal = CalendarApp.getDefaultCalendar();
  const today = new Date();
  const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1);
  const events = cal.getEvents(today, tomorrow, {search: '行銷例會'});
  if (events.length) {
    const event = events[0];
    event.setTitle('【更新】行銷例會');
    event.setDescription('改為 30 分鐘站立會議');
    Logger.log('Updated: ' + event.getId());
  }
}


5.    刪除活動(小心使用)

function deleteEventById(id) {
  // id 可從 Logger 或 UI 擷取;注意 Apps Script 的 getId() 與 API 的 id 格式不同(見下方避雷)
  const cal = CalendarApp.getDefaultCalendar();
  const events = cal.getEvents(new Date('2025-10-12T00:00:00+08:00'), new Date('2025-10-13T00:00:00+08:00'));
  const target = events.find(e => e.getId() === id);
  if (target) target.deleteEvent();
}


操作步驟:從零到部署

步驟 A—快速測試

1.    把「建立一般活動」函式貼上,按執行。

2.    系統要求授權 → 選用同一個 Google 帳號授權。

3.    到 Google 日曆確認是否已建立活動。


步驟 B—與 Google 試算表整合(從表格建立活動)

function createEventsFromSheet() {
  const sheet = SpreadsheetApp.getActive().getSheetByName('events');
  const cal = CalendarApp.getDefaultCalendar();
  const rows = sheet.getDataRange().getValues(); // 標題列: Title, Start, End, Location, Guests
  rows.slice(1).forEach(r => {
    const [title, startStr, endStr, location, guests] = r;
    if (!title || !startStr || !endStr) return;
    const event = cal.createEvent(title, new Date(startStr), new Date(endStr), {
      location,
      guests,
      sendInvites: !!guests
    });
    Logger.log('Created: ' + event.getId());
  });
}


步驟 C—排程自動化(每天 8:00 跑一次)

1.    在 Apps Script 左側「時鐘」圖示 → 新增觸發器。

2.    選擇 createEventsFromSheet → 時間驅動 → 每天 8:00。

3.    儲存。從此表單/試算表一更新,你就能用排程把活動批次寫進主要行事曆。


常見錯誤與雷點

1.  「授權被拒」或看不到建立結果

症狀:第一次執行就停、或跑完日曆沒東西。

原因:沒有完成 OAuth 授權,或你的帳號沒有對該行事曆的寫入權。

解法:重新執行讓授權流程跑完;確定你用的是自己的帳號在跑腳本,並操作自己的主要行事曆(getDefaultCalendar())。若要寫別的共享行事曆,改用 getCalendarById() 並確認對方有給你寫入權限。官方 getDefaultCalendar() 定義請參考文件。

2.   「為什麼 API 文件說用 primary,但我這邊沒有?」

症狀:看別人的範例寫 calendarId="primary",你在 Apps Script 想跟著抄卻出錯。

原因:這是 Calendar API 的參數寫法;你現在用的是 Apps Script 的 CalendarApp。兩邊概念相同但寫法不同。

解法:Apps Script 直接用 CalendarApp.getDefaultCalendar();如果你日後切成「原生 Calendar API」,那就把 calendarId 寫 "primary"。

3.    整天活動「少一天」或日期偏移

症狀:建立整天活動後,發現顯示日期錯一格。

原因:時區或日期物件的建立方式不一致。

解法:整天活動建議用 new Date(年, 月(0-11), 日) 這種「不含時區的在地時間」表達,並確保專案時區與你的 Google 日曆時區一致。

4.    getId() 拿到的 ID 拿去 getEventById() 找不到

症狀:你把 event.getId() 存下來,事後用 CalendarApp.getEventById(id) 卻抓不到。

原因:Apps Script 的事件 ID 與 Calendar API 的事件 ID 格式不完全相同(Apps Script 可能帶有 @google.com 後綴或略有轉換),常見於跨服務混用時。

解法:同一條鏈路用同一個服務(Apps Script ↔︎ Apps Script),或在設計上改用你自己的主鍵(例如「標題 + 起訖時間 + 建立者」)來尋找事件。社群上也曾討論過此類行為差異。

5.    以為「我的 Email 就是 Calendar ID」

症狀:getCalendarById('myname@gmail.com') 偶爾能用、偶爾爆。

原因:主要行事曆的 ID 不保證等於你的 email,且共享行事曆一定不是你的 email。

解法:Apps Script 要主要行事曆就用 getDefaultCalendar();API 要主要行事曆就用 "primary";若一定要 ID,請到「Google 日曆 → 設定 → [行事曆名稱] → 整合行事曆」查看 Calendar ID。

6.    檔案/表單寫入重複建立活動

症狀:同一筆資料被排程重複寫很多次。

原因:沒做「查重」或沒有儲存「已建立」標記。

解法:建立前用 getEvents(start, end, {search: '關鍵字'}) 查一下;或在試算表加一欄「已寫入 = TRUE / eventId」。

7.    觸發器或配額限制

症狀:大量寫入、短時間讀寫爆錯。

原因:Apps Script 與 Calendar 服務都有每日/每分鐘配額。

解法:批次節流、加 Utilities.sleep()、分時段寫入;必要時改用 Calendar API 的批次端點或重試策略(退避)。


進階技巧:把 getDefaultCalendar() 用到位

A. 關鍵字搜尋 + 條件刪改(避免誤傷)

function upsertWeeklyStandup() {
  const cal = CalendarApp.getDefaultCalendar();
  const start = new Date('2025-10-13T09:00:00+08:00');
  const end   = new Date('2025-10-13T09:30:00+08:00');
  const candidates = cal.getEventsForDay(new Date('2025-10-13'), {search: 'Weekly Standup'});
  if (candidates.length) {
    candidates[0].setTime(start, end).setLocation('會議室 B');
  } else {
    cal.createEvent('Weekly Standup', start, end, {location: '會議室 A'});
  }
}


B. 加入提醒(Email / 通知、相對時間)

function createWithReminders() {
  const cal = CalendarApp.getDefaultCalendar();
  const s = new Date('2025-10-20T10:00:00+08:00');
  const e = new Date('2025-10-20T11:00:00+08:00');
  const event = cal.createEvent('客戶簡報', s, e);
  event.addEmailReminder(30);   // 30 分鐘前寄 Email
  event.addPopupReminder(10);   // 10 分鐘前彈出提醒
}


C. 讀取顏色 / 設定顏色(視覺化管理)

function colorCoding() {
  const cal = CalendarApp.getDefaultCalendar();
  const s = new Date(); const e = new Date(s); e.setDate(e.getDate() + 1);
  cal.getEvents(s, e).forEach(ev => {
    if (ev.getTitle().includes('面試')) ev.setColor(CalendarApp.EventColor.RED);
    else if (ev.getTitle().includes('會議')) ev.setColor(CalendarApp.EventColor.BLUE);
  });
}


D. 與共同行事曆協作(何時不用 getDefaultCalendar())

如果你的活動必須寫到共享行事曆,例如「團隊排班」,就改用:

const teamCal = CalendarApp.getCalendarById('team-calendar@group.calendar.google.com');


先確認你有寫入權限;若沒有,就請行事曆擁有者到「設定 → 共用與存取權限」加你為可編輯者。若是 API,對應就用該行事曆的 calendarId,不是 "primary"。


寫法對照:Apps Script vs. Calendar API(觀念釐清)

Apps Script:CalendarApp.getDefaultCalendar() → 直接拿到物件,呼叫 createEvent() 等方法。

Calendar API:HTTP/JSON 介面,路徑像 calendars/primary/events.insert,calendarId 用 "primary" 指主要行事曆。


選擇原則:

想要快速在 Google 生態系(試算表、表單)內搞定 → Apps Script 最省事。

要跨系統、大量批次、服務端整合 → Calendar API 比較彈性。


問題集

Q1:getDefaultCalendar() 會不會拿到別人的行事曆?

不會。它永遠拿「執行腳本的使用者」的主要行事曆。要別人的共享行事曆請用 getCalendarById() 並確認權限。

Q2:我在 API 世界看到 "primary",在 Apps Script 可不可以也這樣寫?

不行。那是 API 的 calendarId 值;在 Apps Script 你就用 CalendarApp.getDefaultCalendar()。

Q3:如何查我的 Calendar ID?

Google 日曆網頁 → 設定 → 選你的行事曆 → 整合行事曆 → Calendar ID。共享行事曆會長得像 xxxx@group.calendar.google.com。

Q4:事件 ID 為什麼跟 API 的不同?

兩邊格式確實有差,跨界混用時常見「找不到」的狀況;同一路徑就用同一套服務,或用自定主鍵查找。


避雷清單

1.    時區不一致:專案時區、日曆時區、日期物件格式不一致會導致偏移。整天活動請用「年月日」建法。

2.    重複建立:排程跑多次沒查重,活動會一直疊。先 getEvents() 搜尋關鍵字或用主鍵判斷。

3.    權限幻想:不是你的行事曆就不能隨意寫。共享行事曆請先拿到「可編輯」權限。

4.    把 Email 當 Calendar ID:主要行事曆 ID 不保證等於你的 email;API 要主要行事曆改寫 "primary",Apps Script 就用 getDefaultCalendar()。

5.    事件 ID 混流:Apps Script 事件 ID 與 API 事件 ID 不同;別跨服務硬拼。

6.    配額忽視:大量建立/修改請分批與退避,避免觸發配額上限。

7.    忘記關閉邀請:大量測試時,如果 sendInvites: true,你的同事會收到一堆信。測試階段關掉或用測試帳號。

8.    觸發器時段:半夜跨日程式跑時,整天活動日期更容易偏。建議固定在白天時段執行。

9.    關鍵字過寬:{search: '會議'} 太廣,容易誤判。請搭配時間窗與更精確的關鍵字。


實務範本庫

範本 1:避免重複建立

function upsertByKey(title, start, end) {
  const cal = CalendarApp.getDefaultCalendar();
  const events = cal.getEvents(start, end, {search: title});
  if (events.some(e => e.getTitle() === title)) return 'skipped';
  cal.createEvent(title, start, end);
  return 'created';
}


範本 2:由表單輸入自動寫入(安裝型觸發器)

function onFormSubmit(e) {
  const cal = CalendarApp.getDefaultCalendar();
  const [title, dateStr, timeStr, durationMin] = e.values; // 依你的表單欄位順序調整
  const start = new Date(`${dateStr} ${timeStr}`);
  const end = new Date(start.getTime() + Number(durationMin) * 60000);
  cal.createEvent(title, start, end);
}


範本 3:批次加提醒與顏色

function massEnhanceToday() {
  const cal = CalendarApp.getDefaultCalendar();
  const start = new Date(); start.setHours(0,0,0,0);
  const end = new Date(start); end.setDate(end.getDate()+1);
  cal.getEvents(start, end).forEach(ev => {
    ev.addPopupReminder(15);
    if (ev.getTitle().includes('外部')) ev.setColor(CalendarApp.EventColor.RED);
  });
}


結語

getDefaultCalendar() 的精神其實很純粹:別再找 ID、別再對焦點猶豫,直接把手邊工作丟進自己主要行事曆。等到真的需要跨團隊、跨系統、共享資源時,再切換成 getCalendarById() 或 Calendar API 的 "primary" 與特定 calendarId。把上面幾個坑避掉,你的自動化流程就穩得很。


張貼留言 (0)
較新的 較舊