高级日历服务

通过高级日历服务,您可以在 Apps 脚本中使用公共 Google Calendar API。与 Apps 脚本的内置日历服务非常相似,此 API 允许脚本访问和修改用户的 Google 日历,包括用户订阅的其他日历。在大多数情况下,内置服务更易于使用,但此高级服务提供了一些额外的功能,包括为各个活动设置背景颜色。

参考

如需详细了解此服务,请参阅公共 Google 日历 API 的参考文档。与 Apps 脚本中的所有高级服务一样,高级日历服务使用的对象、方法和参数均与公共 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);   } }

同步活动

以下示例演示了如何使用同步令牌检索事件。 如果您在日历高级服务请求中添加同步令牌,则生成的响应只会包含自该令牌生成以来发生更改的项目,从而提高处理效率。如需详细了解同步流程,请参阅高效同步资源

以下示例使用了上述示例中定义的相同 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); }