進階日曆服務

進階日曆服務可讓您在 Apps Script 中使用公開的 Google Calendar API。與 Apps Script 的內建日曆服務類似,這個 API 可讓指令碼存取及修改使用者的 Google 日曆,包括使用者訂閱的其他日曆。在大多數情況下,內建服務較容易使用,但這項進階服務提供一些額外功能,包括設定個別活動的背景顏色。

參考資料

如要進一步瞭解這項服務,請參閱公開 Google Calendar API 的參考說明文件。與 Apps Script 中的所有進階服務一樣,進階 Calendar 服務使用的物件、方法和參數,都與公開 API 相同。詳情請參閱「如何判斷方法簽章」。

如要回報問題及尋求其他支援,請參閱日曆支援指南

HTTP 要求標頭

進階日曆服務可接受 HTTP 要求標頭 If-MatchIf-None-Match。詳情請參閱參考說明文件

程式碼範例

下列程式碼範例使用 API 的第 3 版

建立事件

以下範例說明如何在使用者預設日曆中建立活動。

advanced/calendar.gs
/**  * Creates an event in the user's default calendar.  * @see https://developers.google.com/calendar/api/v3/reference/events/insert  */ function createEvent() {   const calendarId = 'primary';   const start = getRelativeDate(1, 12);   const end = getRelativeDate(1, 13);   // event details for creating event.   let event = {     summary: 'Lunch Meeting',     location: 'The Deli',     description: 'To discuss our plans for the presentation next week.',     start: {       dateTime: start.toISOString()     },     end: {       dateTime: end.toISOString()     },     attendees: [       {email: '[email protected]'},       {email: '[email protected]'}     ],     // Red background. Use Calendar.Colors.get() for the full list.     colorId: 11   };   try {     // call method to insert/create new event in provided calandar     event = Calendar.Events.insert(event, calendarId);     console.log('Event ID: ' + event.id);   } catch (err) {     console.log('Failed with error %s', err.message);   } }  /**  * Helper function to get a new Date object relative to the current date.  * @param {number} daysOffset The number of days in the future for the new date.  * @param {number} hour The hour of the day for the new date, in the time zone  *     of the script.  * @return {Date} The new date.  */ function getRelativeDate(daysOffset, hour) {   const date = new Date();   date.setDate(date.getDate() + daysOffset);   date.setHours(hour);   date.setMinutes(0);   date.setSeconds(0);   date.setMilliseconds(0);   return date; }

列出日曆

以下範例說明如何擷取使用者日曆清單中顯示的日曆詳細資料。

advanced/calendar.gs
/**  * Lists the calendars shown in the user's calendar list.  * @see https://developers.google.com/calendar/api/v3/reference/calendarList/list  */ function listCalendars() {   let calendars;   let pageToken;   do {     calendars = Calendar.CalendarList.list({       maxResults: 100,       pageToken: pageToken      });     if (!calendars.items || calendars.items.length === 0) {       console.log('No calendars found.');       return;     }     // Print the calendar id and calendar summary     for (const calendar of calendars.items) {       console.log('%s (ID: %s)', calendar.summary, calendar.id);     }     pageToken = calendars.nextPageToken;   } while (pageToken); }

列出事件

以下範例說明如何列出使用者預設日曆中接下來的 10 個活動。

advanced/calendar.gs
/**  * Lists the next 10 upcoming events in the user's default calendar.  * @see https://developers.google.com/calendar/api/v3/reference/events/list  */ function listNext10Events() {   const calendarId = 'primary';   const now = new Date();   const events = Calendar.Events.list(calendarId, {     timeMin: now.toISOString(),     singleEvents: true,     orderBy: 'startTime',     maxResults: 10   });   if (!events.items || events.items.length === 0) {     console.log('No events found.');     return;   }   for (const event of events.items) {     if (event.start.date) {       // All-day event.       const start = new Date(event.start.date);       console.log('%s (%s)', event.summary, start.toLocaleDateString());       continue;     }     const start = new Date(event.start.dateTime);     console.log('%s (%s)', event.summary, start.toLocaleString());   } }

有條件地修改事件

以下範例說明如何使用 If-Match 標頭,有條件地更新日曆活動。這個指令碼會建立新活動、等待 30 秒,然後更新活動,但前提是活動建立後,活動詳細資料未曾變更。

advanced/calendar.gs
/**  * Creates an event in the user's default calendar, waits 30 seconds, then  * attempts to update the event's location, on the condition that the event  * has not been changed since it was created.  If the event is changed during  * the 30-second wait, then the subsequent update will throw a 'Precondition  * Failed' error.  *  * The conditional update is accomplished by setting the 'If-Match' header  * to the etag of the new event when it was created.  */ function conditionalUpdate() {   const calendarId = 'primary';   const start = getRelativeDate(1, 12);   const end = getRelativeDate(1, 13);   let event = {     summary: 'Lunch Meeting',     location: 'The Deli',     description: 'To discuss our plans for the presentation next week.',     start: {       dateTime: start.toISOString()     },     end: {       dateTime: end.toISOString()     },     attendees: [       {email: '[email protected]'},       {email: '[email protected]'}     ],     // Red background. Use Calendar.Colors.get() for the full list.     colorId: 11   };   event = Calendar.Events.insert(event, calendarId);   console.log('Event ID: ' + event.getId());   // Wait 30 seconds to see if the event has been updated outside this script.   Utilities.sleep(30 * 1000);   // Try to update the event, on the condition that the event state has not   // changed since the event was created.   event.location = 'The Coffee Shop';   try {     event = Calendar.Events.update(         event,         calendarId,         event.id,         {},         {'If-Match': event.etag}     );     console.log('Successfully updated event: ' + event.id);   } catch (e) {     console.log('Fetch threw an exception: ' + e);   } }

有條件地擷取事件

以下範例說明如何使用 If-None-Match 標頭,有條件地擷取日曆活動。指令碼會建立新事件,然後輪詢事件的變更 30 秒。每當活動變更時,系統就會擷取新版本。

advanced/calendar.gs
/**  * Creates an event in the user's default calendar, then re-fetches the event  * every second, on the condition that the event has changed since the last  * fetch.  *  * The conditional fetch is accomplished by setting the 'If-None-Match' header  * to the etag of the last known state of the event.  */ function conditionalFetch() {   const calendarId = 'primary';   const start = getRelativeDate(1, 12);   const end = getRelativeDate(1, 13);   let event = {     summary: 'Lunch Meeting',     location: 'The Deli',     description: 'To discuss our plans for the presentation next week.',     start: {       dateTime: start.toISOString()     },     end: {       dateTime: end.toISOString()     },     attendees: [       {email: '[email protected]'},       {email: '[email protected]'}     ],     // Red background. Use Calendar.Colors.get() for the full list.     colorId: 11   };   try {     // insert event     event = Calendar.Events.insert(event, calendarId);     console.log('Event ID: ' + event.getId());     // Re-fetch the event each second, but only get a result if it has changed.     for (let i = 0; i < 30; i++) {       Utilities.sleep(1000);       event = Calendar.Events.get(calendarId, event.id, {}, {'If-None-Match': event.etag});       console.log('New event description: ' + event.start.dateTime);     }   } catch (e) {     console.log('Fetch threw an exception: ' + e);   } }

同步處理活動

以下範例說明如何使用同步權杖擷取事件。 在 Calendar 進階服務要求中加入同步權杖時,產生的回應只會包含自產生該權杖以來變更的項目,因此處理效率更高。如要進一步瞭解同步處理程序,請參閱「有效率地同步處理資源」。

下列範例使用與上述範例中定義的相同 getRelativeDate(daysOffset, hour) 方法。

advanced/calendar.gs
/**  * Retrieve and log events from the given calendar that have been modified  * since the last sync. If the sync token is missing or invalid, log all  * events from up to a month ago (a full sync).  *  * @param {string} calendarId The ID of the calender to retrieve events from.  * @param {boolean} fullSync If true, throw out any existing sync token and  *        perform a full sync; if false, use the existing sync token if possible.  */ function logSyncedEvents(calendarId, fullSync) {   const properties = PropertiesService.getUserProperties();   const options = {     maxResults: 100   };   const syncToken = properties.getProperty('syncToken');   if (syncToken && !fullSync) {     options.syncToken = syncToken;   } else {     // Sync events up to thirty days in the past.     options.timeMin = getRelativeDate(-30, 0).toISOString();   }   // Retrieve events one page at a time.   let events;   let pageToken;   do {     try {       options.pageToken = pageToken;       events = Calendar.Events.list(calendarId, options);     } catch (e) {       // Check to see if the sync token was invalidated by the server;       // if so, perform a full sync instead.       if (e.message === 'Sync token is no longer valid, a full sync is required.') {         properties.deleteProperty('syncToken');         logSyncedEvents(calendarId, true);         return;       }       throw new Error(e.message);     }     if (events.items && events.items.length === 0) {       console.log('No events found.');       return;     }     for (const event of events.items) {       if (event.status === 'cancelled') {         console.log('Event id %s was cancelled.', event.id);         return;       }       if (event.start.date) {         const start = new Date(event.start.date);         console.log('%s (%s)', event.summary, start.toLocaleDateString());         return;       }       // Events that don't last all day; they have defined start times.       const start = new Date(event.start.dateTime);       console.log('%s (%s)', event.summary, start.toLocaleString());     }     pageToken = events.nextPageToken;   } while (pageToken);   properties.setProperty('syncToken', events.nextSyncToken); }