Enforce recurring no-meeting deep-work days
Block focus days automatically — Northwind teammates own Wednesdays.
Published Aug 17, 2025
Northwind tried to protect deep-work Wednesdays the polite way — a Slack announcement, a calendar note, a gentle reminder in the team handbook. Within three weeks the focus day was speckled with “quick syncs” because nobody could see at a glance that the day was claimed. The fix is to make Wednesday look busy on every shared calendar, the same way any other commitment does.
This script walks the next few weeks of your default calendar, finds every Wednesday, and drops an all-day “Deep work day — no meetings” event on each one. It is idempotent — it checks for an existing block before adding another — so you can run it as often as you like and it will only ever fill the gaps.
What you’ll need
- Edit access to a Google Calendar (the script uses your default calendar).
- A consistent name for the block — change
BLOCK_TITLEbelow if you prefer “Focus day” or something more on-brand. - Nothing else. No API key, no spreadsheet, no add-on.
The script
// The title used on every created event. Used both as the event title
// and as the search term that detects "already blocked" days, so keep
// it consistent once you have run the script.
const BLOCK_TITLE = 'Deep work day — no meetings';
// 3 = Wednesday in the JavaScript Date convention (Sunday is 0).
const FOCUS_DAY_OF_WEEK = 3;
// How many weeks of Wednesdays to block in one run. Eight weeks is a
// comfortable window — short enough to re-run monthly, long enough that
// nobody books "the Wednesday just after the script's horizon".
const DEFAULT_WEEKS_AHEAD = 8;
/**
* Walks the next `weeksAhead` weeks and creates an all-day deep-work
* block on every Wednesday that does not already have one.
*
* @param {number} [weeksAhead] How many weeks ahead to fill.
*/
function blockWednesdays(weeksAhead = DEFAULT_WEEKS_AHEAD) {
const cal = CalendarApp.getDefaultCalendar();
// 1. Start at midnight today so the day comparison is stable no matter
// when the trigger fires.
const start = new Date();
start.setHours(0, 0, 0, 0);
let created = 0;
for (let i = 0; i < weeksAhead * 7; i++) {
// 2. Walk one day at a time. A simple loop is easier to reason about
// than jumping by seven days from "the next Wednesday".
const d = new Date(start);
d.setDate(start.getDate() + i);
if (d.getDay() !== FOCUS_DAY_OF_WEEK) continue;
// 3. Skip days that already carry the block. `getEventsForDay` with a
// search term is cheap and avoids the duplicate-events smell.
const exists = cal.getEventsForDay(d, { search: BLOCK_TITLE });
if (exists.length) continue;
// 4. Create the all-day block. All-day events sit at the top of the
// day strip in Google Calendar — that visibility is the whole point.
cal.createAllDayEvent(BLOCK_TITLE, d);
created++;
}
Logger.log('Created ' + created + ' deep-work blocks.');
}
How it works
blockWednesdaysopens your default calendar and normalises “today” to midnight, so the day-of-week check is not skewed by the trigger’s clock time.- It walks one day at a time across the window —
weeksAhead * 7iterations — and skips any day whosegetDay()is not Wednesday. - For each Wednesday it calls
getEventsForDaywith the block title as the search term. If anything comes back, the day is already protected and the script moves on. - Otherwise it creates an all-day event with the same title. All-day events are deliberately chosen over a long timed event because they collapse into a single strip at the top of the day, leaving the timeline itself visually empty for the focus session.
Example run
Suppose today is Monday 18 August 2025 and you run the script with the default eight-week window. The first run creates a block on every Wednesday between 20 August and 8 October:
| Date | Action |
|---|---|
| Wed 20 Aug | Created “Deep work day — no meetings” |
| Wed 27 Aug | Created “Deep work day — no meetings” |
| Wed 3 Sep | Skipped (already blocked) |
| Wed 10 Sep | Created “Deep work day — no meetings” |
A second run an hour later does nothing — every Wednesday in the window
already has a matching event, so getEventsForDay returns a hit each time and
the loop short-circuits.
Trigger it
This is a low-frequency, idempotent job, so a time-driven trigger that fires once a month is plenty:
- In the Apps Script editor, open Triggers.
- Add a new trigger for
blockWednesdays, event source Time-driven, type Month timer, on the first of the month.
If you prefer weekly, set it to Week timer on Sunday — the script will still only create blocks where they are missing, so re-runs are safe.
Watch out for
- The search match is on the exact
BLOCK_TITLEstring. If you rename the block in the future, the script will treat the renamed events as missing and cheerfully add a second block alongside them. Pick a title you can live with, or update both places at once. - All-day events still show as “free” on the busy/free overlay by default. If colleagues book over the day anyway, mark the event as Busy under Visibility in the calendar UI — Apps Script cannot change that flag retroactively.
- The default calendar is whichever calendar the script owner counts as
default. If you want to block a shared team calendar instead, swap
getDefaultCalendar()forgetCalendarById('...')and grant the script access to that calendar. - The block does not stop people sending you invitations — it simply tells them you are unavailable. Pair it with a polite auto-decline rule if your team needs harder enforcement.
Related
Schedule personal habits and routines
Block recurring habits on Awadesh's calendar — gym, walks, deep-work mornings.
Updated Nov 5, 2025
Sync birthdays and anniversaries to Calendar
Populate recurring personal dates from a Sheet — the Northwind team rituals calendar.
Updated Nov 1, 2025
Generate recurring events with custom exceptions
Handle complex recurrence rules in code — every Tuesday except UK bank holidays.
Updated Oct 28, 2025
Auto-reschedule low-priority conflicts
Move flexible Northwind events around fixed ones — focus blocks bend, client calls don't.
Updated Oct 16, 2025
Build a contract-renewal calendar
Track Northwind's recurring-revenue renewal dates as calendar events for proactive sales.
Updated Oct 8, 2025