Auto-block focus time around meetings
Reserve prep and recovery slots automatically around every Northwind meeting.
Published Jun 30, 2025
Back-to-back meetings leave no room to prepare for the next one or to write up the last. At Northwind, the fix is a discipline nobody keeps up by hand: blocking fifteen minutes before each meeting to get your head in, and fifteen after to capture actions before they evaporate. Add it manually and you forget; skip it and your day becomes a wall of calls.
This automation looks ahead seven days and, for every real meeting on your calendar, drops a short “Focus — prep” block before it and a “Focus — recovery” block after it. It tags each meeting once it has been processed, so running it again never doubles up the blocks.
What you’ll need
- Access to your default Google Calendar — the script uses
CalendarApp.getDefaultCalendar(), so there is nothing to configure beyond the two duration constants. - That is it. The script creates and tags its own events.
The script
// Minutes of prep time to reserve before each meeting.
const PREP_MINUTES = 15;
// Minutes of recovery time to reserve after each meeting.
const RECOVERY_MINUTES = 15;
// How many days ahead to scan for meetings.
const LOOKAHEAD_DAYS = 7;
// Tag key used to mark a meeting as already processed.
const PROCESSED_TAG = 'focus-blocked';
/**
* Scans the next week of calendar events and reserves a prep block before
* and a recovery block after every meeting that has not been processed yet.
*/
function blockFocusTime() {
// 1. Define the window: now through LOOKAHEAD_DAYS from now.
const start = new Date();
const end = new Date(start.getTime() + LOOKAHEAD_DAYS * 86400000);
const cal = CalendarApp.getDefaultCalendar();
const events = cal.getEvents(start, end);
if (!events.length) {
Logger.log('No events in the next ' + LOOKAHEAD_DAYS + ' days.');
return;
}
let blocked = 0;
for (const event of events) {
// 2. Skip the focus blocks themselves so we never block our blocks.
if (event.getTitle().startsWith('Focus')) continue;
// 3. Skip meetings already processed on a previous run.
if (event.getTag(PROCESSED_TAG) === 'yes') continue;
// 4. Create the prep block immediately before the meeting.
cal.createEvent('Focus — prep',
new Date(event.getStartTime().getTime() - PREP_MINUTES * 60000),
event.getStartTime(),
{ description: 'Prep for ' + event.getTitle() });
// 5. Create the recovery block immediately after the meeting.
cal.createEvent('Focus — recovery',
event.getEndTime(),
new Date(event.getEndTime().getTime() + RECOVERY_MINUTES * 60000),
{ description: 'Recovery after ' + event.getTitle() });
// 6. Tag the meeting so the next run leaves it alone.
event.setTag(PROCESSED_TAG, 'yes');
blocked++;
}
Logger.log('Added focus blocks around ' + blocked + ' meetings.');
}
How it works
blockFocusTimebuilds a time window from now to seven days ahead and fetches every event in it from the default calendar.- If the window is empty, it logs a message and stops.
- For each event it skips anything whose title starts with “Focus” — these are the blocks the script itself created, and processing them would spiral.
- It skips any meeting already carrying the
focus-blockedtag, which is what makes repeat runs safe. - It creates a “Focus — prep” event ending exactly when the meeting starts,
PREP_MINUTESlong, with a description naming the meeting. - It creates a “Focus — recovery” event starting exactly when the meeting
ends,
RECOVERY_MINUTESlong. - It tags the meeting with
focus-blocked: yesso the next run ignores it, and logs how many meetings were handled.
Example run
Your calendar for tomorrow holds one untagged meeting:
| Event | Start | End |
|---|---|---|
| Client review — Acme | 14:00 | 15:00 |
After a run, two new events bracket it:
| Event | Start | End |
|---|---|---|
| Focus — prep | 13:45 | 14:00 |
| Client review — Acme | 14:00 | 15:00 |
| Focus — recovery | 15:00 | 15:15 |
Run the script again and nothing changes — the meeting now carries the
focus-blocked tag, so it is skipped.
Trigger it
This works best running quietly each morning so new meetings get bracketed soon after they appear:
- In the Apps Script editor, open Triggers (the clock icon).
- Click Add Trigger.
- Choose
blockFocusTime, event source Time-driven, type Day timer, and pick an early-morning hour.
Watch out for
- The script does not check whether a slot is already busy. If a meeting starts right after another, the prep block will overlap the earlier meeting rather than find free time.
- Tags travel with the meeting. If you reschedule a tagged meeting, its old prep and recovery blocks stay where they were and no new ones are made — delete the stale blocks or clear the tag to re-process.
- Cancelled or deleted meetings leave their focus blocks behind. Nothing cleans them up automatically.
- Only the default calendar is scanned. Meetings on a secondary or shared
calendar are ignored unless you point
CalendarAppat that calendar. - All-day events have a start and end too, so an all-day item will pick up
midnight-adjacent focus blocks. Add a check for
isAllDayEvent()if that is a problem.
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