appscript.dev
Automation Beginner Calendar Gmail

Email yourself a weekly calendar summary

Send a Monday week-ahead digest of every Northwind meeting in the next seven days.

Published Jun 26, 2025

Mondays at Northwind tend to start with a scroll through the calendar to figure out what the week looks like — which mornings are blocked, which afternoons are clear, when the back-to-back days are. The calendar already knows all of that, but reading it day by day takes longer than it should and leaves you with no record once you have closed the tab.

This short script does the scroll for you. It fetches the next seven days of events, groups them by day, and emails the digest as plain text. A glance at your inbox on Monday morning tells you the shape of the week — and the email sits in the thread as a reference you can search later.

What you’ll need

  • Access to the calendar you want to summarise. The script uses CalendarApp.getDefaultCalendar(); replace with getCalendarById for a shared one.
  • Gmail access for the digest itself — sent to Session.getActiveUser().getEmail().
  • Nothing else — no spreadsheet, no API keys.

The script

// How many days the digest covers. Seven gives a full week ahead.
const DIGEST_DAYS = 7;

// Milliseconds in a day, used to build the end of the window.
const ONE_DAY_MS = 24 * 60 * 60 * 1000;

/**
 * Reads the next seven days of the default calendar and emails a digest,
 * grouped by day, to the script owner.
 */
function weeklyCalendarDigest() {
  const start = new Date();
  const end = new Date(start.getTime() + DIGEST_DAYS * ONE_DAY_MS);

  // 1. Pull every event in the next seven days.
  const events = CalendarApp.getDefaultCalendar().getEvents(start, end);

  // 2. Bucket events by their day. A Map keeps insertion order so days
  //    appear chronologically in the digest.
  const tz = Session.getScriptTimeZone();
  const byDay = new Map();
  for (const e of events) {
    const day = Utilities.formatDate(e.getStartTime(), tz, 'EEEE d MMMM');
    if (!byDay.has(day)) byDay.set(day, []);
    const time = Utilities.formatDate(e.getStartTime(), tz, 'HH:mm');
    byDay.get(day).push(`${time}  ${e.getTitle()}`);
  }

  // 3. Render each day as a header followed by indented event lines.
  //    If the week is empty, send a one-line "all clear" message.
  const body = [...byDay.entries()]
    .map(([day, list]) => `${day}\n${list.map((l) => `  ${l}`).join('\n')}`)
    .join('\n\n') || 'Clear week ahead.';

  // 4. Send the digest to the script owner.
  GmailApp.sendEmail(
    Session.getActiveUser().getEmail(),
    `Week ahead — ${events.length} events`,
    body
  );
}

How it works

  1. weeklyCalendarDigest builds a window from now to seven days ahead (DIGEST_DAYS).
  2. It fetches every event in the window via the default calendar.
  3. It walks the events and groups them into a Map keyed by day string ("Monday 1 September"), preserving order. Each value is a list of HH:mm Title lines.
  4. It renders the map into plain text — one header per day, two-space indented lines underneath, a blank line between days. An empty week gets the fallback message Clear week ahead..
  5. It emails the result to the script owner, with the event count in the subject so the digest is searchable later.

Example run

After a run on Monday morning, the email looks roughly like this:

Subject: Week ahead — 6 events

Monday 1 September   09:00 Production brief   14:00 Client call: Fabrikam

Tuesday 2 September   11:00 One-to-one: Alex

Wednesday 3 September   10:00 Workshop prep   15:30 Edit review

Friday 5 September   09:30 All-hands

Days without events are skipped — Thursday simply does not appear. That keeps the digest short on slow weeks.

Trigger it

Run this once a week, early on Monday:

  1. In the Apps Script editor, open Triggers (the clock icon).
  2. Add a trigger for weeklyCalendarDigest, time-driven, week timer, Monday, between 7am and 8am.
  3. Save and approve the calendar and Gmail authorisation prompts.

Watch out for

  • All-day events are included. They show up with a start time of 00:00, which is usually fine but can clutter the top of each day’s block. Filter with isAllDayEvent() if you want them out — or put them in their own section at the end.
  • The script uses the script’s timezone via Session.getScriptTimeZone(). If you travel or share the script with people in other timezones, set it explicitly in Project Settings so the times come out right.
  • Long event titles wrap awkwardly in plain text. If you want a richer digest, swap GmailApp.sendEmail for the four-argument form with an htmlBody option and render an HTML table instead.
  • The seven-day window includes today. If you would rather start at midnight tonight, set start.setHours(0, 0, 0, 0) and add a day to the end before fetching.

Related