// Calendar · Apps Script

List today's calendar events in Google Calendar.

Use Apps Script's getEvents(start, end) to pull every event from your Google Calendar for today, including the half-open interval gotcha that silently drops all-day events.

I want to write an Apps Script that logs or processes every calendar event happening today, and I keep getting incomplete results.

The script

copy · paste · trigger
listTodayEvents.gs
Apps Script
// List every event on the default calendar for today
// Run manually or attach to a time-driven trigger
function listTodayEvents() {
  var cal = CalendarApp.getDefaultCalendar();

  var start = new Date();
  start.setHours(0, 0, 0, 0);

  var end = new Date(start);
  end.setDate(end.getDate() + 1); // tomorrow at midnight

  var events = cal.getEvents(start, end);

  for (var i = 0; i < events.length; i++) {
    var ev = events[i];
    Logger.log(ev.getTitle() + ' | ' + ev.getStartTime() + ' — ' + ev.getEndTime());
  }

  Logger.log('Total events today: ' + events.length);
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Building the time window

CalendarApp.getEvents(start, end) takes two Date objects. The interval is half-open: start is inclusive, end is exclusive. That means if you pass midnight-tonight as end, any event that starts exactly at midnight tonight is excluded, which is what you want. Where people trip up is setting end to the same value as start, or to the current time instead of midnight, and then wondering why the afternoon is missing.

The safe pattern is to zero out start with setHours(0, 0, 0, 0) — that resets hours, minutes, seconds, and milliseconds to zero — then copy that date and advance one calendar day with setDate(getDate() + 1). You end up with today-at-00:00:00.000 to tomorrow-at-00:00:00.000, which covers the full day.

I keep this date-building block in a small utils file because I reuse it in digest scripts, billing summaries, and daily standup bots. Writing it inline each time invites the copy-paste drift where someone adjusts one variable but not the other.

Why all-day events go missing

All-day events are stored differently from timed events. Google treats them as spanning from midnight to midnight in the calendar's time zone, but the exact millisecond boundary depends on how the Calendar API converts that to the script's execution time zone.

If your Apps Script project's time zone (set in Project Settings) does not match the calendar's time zone, an all-day event that covers today can land just outside your window. The end you computed is midnight in the script's zone; the event boundary is midnight in the calendar's zone. A one-hour offset is enough to drop it.

The fix is either to align time zones, or to use cal.getAllDayEvents(start) instead of getEvents when you only want all-day events. getAllDayEvents takes a single Date and returns everything marked all-day that overlaps that calendar date, regardless of time zone arithmetic. For a script that needs both timed and all-day events, call getEvents with a window that is slightly wider — start one hour before today's midnight, end one hour after tomorrow's midnight — but that introduces its own edge cases near DST transitions. Aligning time zones is cleaner.

Targeting a specific calendar instead of the default

CalendarApp.getDefaultCalendar() returns whichever calendar is tied to the account running the script. If you are sharing the script with teammates or deploying it as a service account, the default calendar may not be the one you intended.

To target a calendar by name, use CalendarApp.getCalendarsByName('Team Standups')[0]. To target by ID (more reliable, since names can collide), use CalendarApp.getCalendarById('your-calendar-id@group.calendar.google.com'). The ID is visible in Google Calendar under Settings for that calendar, labeled Calendar ID.

One thing I have watched bite people: getCalendarsByName returns an array, and if the name matches zero calendars the array is empty. Calling [0] on an empty array gives undefined, and the subsequent getEvents call throws a confusing TypeError. Add a length check before dereferencing.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Why does my script return zero events even though I have meetings today?
The most common cause is a bad time window. Log start and end before calling getEvents and confirm start is today at 00:00:00 and end is tomorrow at 00:00:00. A second cause is time zone mismatch: check that the Apps Script project time zone (Project Settings > Time zone) matches the calendar's time zone.
How do I get the event's meeting link or conference data?
CalendarEvent does not expose Google Meet links directly via a clean method. Use event.getDescription() and parse for the meet.google.com URL, or switch to the Calendar advanced service (Calendar.Events.get) which returns the conferenceData object with the entryPoints array including the video join URL.
Can I filter events by a specific calendar color or category?
No. getEvents returns all events in the window with no server-side filtering by color or category. You filter client-side: iterate the returned array and call event.getColor() to get a CalendarApp.EventColor enum value, then skip events that do not match.
How do I run this script automatically every morning?
In the Apps Script editor open Triggers (clock icon), add a new trigger for listTodayEvents, set event source to Time-driven, type to Day timer, and pick a time window like 7–8 AM. The script runs once in that window each day at a time Google chooses within the window, so do not rely on it firing at exactly 7:00.