appscript.dev
Automation Intermediate Calendar

Auto-decline meetings outside working hours

Protect Northwind schedules — decline invites that land outside 9-6 London time.

Published Jul 20, 2025

Northwind works with clients across several time zones, which is fine until the invites start arriving for 7am or a Sunday afternoon. Each one is easy to decline by hand, but they add up, and a meeting left un-declined quietly blocks out personal time on the calendar.

This script enforces a simple rule: meetings should sit inside working hours. It scans the next fortnight of invites and automatically declines anything that starts before 9am, at or after 6pm, or on a weekend — measured in London time. Invites you have already accepted are left alone; only fresh, still-pending invites get the treatment.

What you’ll need

  • Nothing to set up — the script runs against your default Google Calendar.
  • A clear idea of your working hours. The defaults are 9am to 6pm, Monday to Friday, London time. Adjust the constants below to match yours.

The script

// The time zone working hours are measured in.
const TZ = 'Europe/London';

// Working day boundaries (24-hour clock). Meetings starting before
// START_HOUR or at/after END_HOUR are declined.
const START_HOUR = 9;
const END_HOUR = 18;

// Last working day of the week, as an ISO day number (Mon=1 ... Sun=7).
const LAST_WORK_DAY = 5;

// How far ahead to scan, in milliseconds (14 days).
const WINDOW_MS = 14 * 86400000;

/**
 * Scans the next two weeks of calendar invites and declines any that
 * start outside working hours, in London time.
 */
function declineOutOfHours() {
  // 1. Build the scan window: now until two weeks from now.
  const start = new Date();
  const end = new Date(start.getTime() + WINDOW_MS);
  const events = CalendarApp.getDefaultCalendar().getEvents(start, end);

  for (const event of events) {
    // 2. Only act on invites still awaiting a response. Skip anything
    //    already accepted, declined or that you own.
    if (event.getMyStatus() !== CalendarApp.GuestStatus.INVITED) continue;

    // 3. Read the start hour and ISO weekday in the chosen time zone.
    const hour = parseInt(Utilities.formatDate(event.getStartTime(), TZ, 'H'));
    const day = parseInt(Utilities.formatDate(event.getStartTime(), TZ, 'u'));

    // 4. Decline if it falls on a weekend or outside working hours.
    if (day > LAST_WORK_DAY || hour < START_HOUR || hour >= END_HOUR) {
      event.setMyStatus(CalendarApp.GuestStatus.NO);
      Logger.log('Declined out-of-hours invite: ' + event.getTitle());
    }
  }
}

How it works

  1. declineOutOfHours builds a 14-day window starting from now and reads every event in it from the default calendar.
  2. For each event it checks getMyStatus(). Only events with the status INVITED — invites you have neither accepted nor declined — are considered. Meetings you own or have already responded to are skipped.
  3. It formats the event’s start time into two numbers using the Europe/London time zone: the hour on a 24-hour clock (H) and the ISO weekday (u), where Monday is 1 and Sunday is 7.
  4. If the weekday is past Friday, or the hour is before 9 or at/after 18, the meeting falls outside working hours and setMyStatus declines it. Each decline is logged so you can review what was rejected.

Example run

Suppose four invites are sitting un-answered in the next two weeks:

InviteStart (London)Outcome
Client syncTue 10:00Kept — inside hours
Early standupWed 08:00Declined — before 9am
Evening reviewThu 19:30Declined — at/after 6pm
Weekend catch-upSat 14:00Declined — weekend

After the run, only the Tuesday client sync still shows as pending; the other three are declined and the organisers are notified, exactly as if you had clicked “No” yourself.

Trigger it

Run this on an hourly time-based trigger so invites are handled soon after they arrive:

  1. In the Apps Script editor open Triggers (the clock icon).
  2. Add a trigger for declineOutOfHours, Time-driven, Hour timer, every hour.

An hourly cadence keeps the lag short — a 7am invite sent today is declined well before tomorrow morning, giving the organiser plenty of time to re-book.

Watch out for

  • Declining sends a notification to the organiser. That is the point, but it means a mis-set START_HOUR or END_HOUR will fire off real decline emails to clients. Test with Logger.log before enabling the trigger.
  • The check is purely on start time. A meeting starting at 5:30pm that runs to 7pm is kept; one starting at 6:01pm is declined. Adjust END_HOUR if you want a stricter cut-off.
  • Declines are not reversible by the script. If it wrongly declines a meeting you wanted, you must re-accept it manually — the script only ever sets status to NO, never back to YES.
  • All-day events have a start time of midnight, so they will be declined as “before 9am”. Add an event.isAllDayEvent() skip if all-day invites should be left alone.
  • The window is two weeks. An invite for a meeting more than 14 days out is not seen until it falls inside the window, so it may sit pending for a while before being declined.

Related