Auto-reschedule low-priority conflicts
Move flexible Northwind events around fixed ones — focus blocks bend, client calls don't.
Published Oct 16, 2025
A Northwind designer’s week is a mix of two kinds of event: fixed commitments like client calls and reviews, and flexible work like focus blocks and admin time. When a new client call lands on top of a focus block, the focus block should give way — but only a person noticing the clash and dragging it does that. Left alone, the calendar shows a double-booking and the focus block quietly never happens.
This script enforces the rule automatically. Flexible events are marked with a
[flex] prefix in their title. The script scans the week, finds any overlap
between a flexible event and another event, and slides the flexible one to just
after the thing it collided with. Fixed events never move.
What you’ll need
- The Google Calendar you want managed — the script uses your default calendar.
- A naming convention: every flexible, movable event has a title starting with
[flex], for example[flex] Focus blockor[flex] Admin. - Nothing else. Anything without the
[flex]prefix is treated as fixed and left alone.
The script
// Title prefix that marks an event as flexible (movable).
const FLEX_PREFIX = '[flex]';
// How far ahead to scan for conflicts, in days.
const SCAN_DAYS = 7;
// Gap to leave after a fixed event before a moved flexible one, in minutes.
const BUFFER_MINUTES = 5;
/**
* Scans the next week, finds overlaps between a flexible event and
* its neighbour, and shifts the flexible event clear of the conflict.
*/
function rescheduleFlexible() {
const start = new Date();
const end = new Date(start.getTime() + SCAN_DAYS * 86400000);
// Pull the week's events, sorted earliest-start first.
const events = CalendarApp.getDefaultCalendar()
.getEvents(start, end)
.sort((a, b) => a.getStartTime() - b.getStartTime());
if (events.length < 2) {
Logger.log('Fewer than two events — no conflicts possible.');
return;
}
// Compare each event with the one immediately after it.
for (let i = 0; i < events.length - 1; i++) {
const a = events[i];
const b = events[i + 1];
// No overlap: a ends before (or as) b starts — nothing to do.
if (a.getEndTime() <= b.getStartTime()) continue;
// They overlap. Move whichever one is flexible; if both are flexible
// move the later one, if neither is, leave the clash for a human.
if (a.getTitle().startsWith(FLEX_PREFIX)) {
shiftAfter(a, b);
} else if (b.getTitle().startsWith(FLEX_PREFIX)) {
shiftAfter(b, a);
}
}
}
/**
* Moves the flexible event to start just after the fixed event ends,
* keeping the flexible event's original duration.
*/
function shiftAfter(flex, fixed) {
const duration = flex.getEndTime() - flex.getStartTime();
const newStart = new Date(
fixed.getEndTime().getTime() + BUFFER_MINUTES * 60000
);
flex.setTime(newStart, new Date(newStart.getTime() + duration));
}
How it works
rescheduleFlexibleworks out a window — now toSCAN_DAYSahead — and reads every event in it from the default calendar.- It sorts the events by start time, so each event sits next to its nearest neighbour.
- If there are fewer than two events there can be no conflict, so it stops.
- It walks the list comparing each event
awith the next eventb. Ifaends on or beforebstarts there is no overlap and it moves on. - When two events overlap it checks the
[flex]prefix: ifais flexible it movesa; otherwise ifbis flexible it movesb. If neither is flexible the clash is between two fixed events and is left for a person to resolve. shiftAftermeasures the flexible event’s duration, sets its new start toBUFFER_MINUTESafter the fixed event ends, and applies a matching new end — so the event keeps its length and just slides later.
Example run
The calendar before a run, on one day:
| Event | Time |
|---|---|
[flex] Focus block | 10:00 – 12:00 |
| Client call: Acme | 11:00 – 11:30 |
The focus block and the client call overlap. After rescheduleFlexible:
| Event | Time |
|---|---|
| Client call: Acme | 11:00 – 11:30 |
[flex] Focus block | 11:35 – 13:35 |
The client call stays put; the focus block keeps its two-hour length and moves to start five minutes after the call ends.
Trigger it
Run this on a daily schedule so the week stays clean as new events land:
- In the Apps Script editor open Triggers (the clock icon).
- Add a trigger for
rescheduleFlexible, Time-driven, Day timer, set for early morning before the working day starts. - Approve the authorisation prompt. Flexible events now get nudged clear of conflicts every morning.
Watch out for
- The script only compares each event with its immediate neighbour. Moving a flexible event can push it onto a later event — run the trigger again, or loop until no overlaps remain, for dense calendars.
- A moved flexible event keeps its duration but not its preferred time of day. A morning focus block can land in the afternoon if the day is busy.
- Only events with the exact
[flex]prefix are movable. A typo in the title means the event is treated as fixed and never moved. - Two overlapping fixed events are left untouched by design — the script will not choose between two real commitments.
- The script edits your live calendar with no undo. Test it on a quiet week, or on a secondary calendar, before trusting it with your main one.
- All-day events have no real start and end time in the usual sense and can produce odd comparisons. Exclude them if your calendar uses them.
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
Build a contract-renewal calendar
Track Northwind's recurring-revenue renewal dates as calendar events for proactive sales.
Updated Oct 8, 2025
Apply attendee rules by event type
Auto-invite the right Northwind people from a Sheet — design reviews get design, client calls get account leads.
Updated Oct 4, 2025