Bulk Calendar Import Offset by One Day? Here's How I Fixed 157 Events in 10 Seconds with a Javascript Snippet
From Project Planning Hiccup to Simple Adjustment
Picture this: you've just bulk-imported 157 events into your Google Calendar. You sit back, admire your work, and then.. whoops. Every single event is off by one day. You forgot about that Federal holiday. Maybe a row got misaligned in your CSV. Maybe the overnight PST/EST timezones conspired against you. Either way, you're not about to click through 157 events to adjust them one by one like some sort of calendar hamster wheeel.
In this post, I'll walk you through how I solved this exact problem using Google Apps Script: a handy tool from Good ol' Google that lets you run JavaScript directly in the Google workspace environment to automate tasks across their suite of products. We're going to build a script that detects every event in any given calendar based on the CalendarID and shifts them all back by exactly one day.
Ok, so let's get into it.
Quick Google Apps Script Config
Because we're targeting Google Calendar, we'll be using Google Apps Script. Think of it as JavaScript that lives inside Google's ecosystem, with built-in access to Calendar, Sheets, Drive, and more. No external APIs, no authentication headaches just pure automation magic.
To get started, head to script.google.com and create a new project. This is where we'll kick off our calendar-shifting script one shot.
Step 1: Get Your Target Calendar ID
Every Google Calendar has a unique identifier. You'll need this to tell the script exactly which calendar to modify.
const CALENDAR_ID = "[email protected]";
Where to find it: Open Google Calendar → Settings → Select your calendar → Scroll down to "Integrate calendar" → Copy the Calendar ID.
Pro tip: Don't accidentally grab your primary calendar ID unless you really want to shift everything in your life back a day. Been there. Make sure to do this one a sandbox calendar.
Step 2: The Safety Net (Dry Run Mode)
Before we start time-traveling events, we need a safety toggle. This is where the DRY_RUN variable basically becomes your best friend.
const DRY_RUN = true;
When set to true, the script will only log what it would change, there are no actual modifications that will happen against your datasource. Like dress rehearsal before the big show: Always run in dry mode first.
Set this to false when you're ready to commit and actually move those events.
Step 3: Grab the Calendar
With our calendar ID in hand, we call upon Google's CalendarApp to retrieve the calendar object:
const cal = CalendarApp.getCalendarById(CALENDAR_ID);
if (!cal) throw new Error("Calendar not found. Check CALENDAR_ID.");
If the calendar isn't found, the script throws an error. This saved me during my live demo when I accidentally pasted the wrong ID—classic live coding moment.
https://youtu.be/Iu7n6R7FGEU?si=-lEM0SFEU7wdE3gk
Step 4: Fetching All Events in the Range
Here's where it gets interesting. We don't know the exact date range of your events, so we cast an absurdly wide net:
const FAR_PAST = new Date(2025, 0, 1); // Jan 1, 2025
const FAR_FUTURE = new Date(2027, 0, 1); // Jan 1, 2027
const events = cal.getEvents(FAR_PAST, FAR_FUTURE);
This fetches every event in the calendar between the year 2025 and 2027. It will only return events that actually exist, so there's no penalty for going wide. It's like telling the script, "Just grab everything in this range."
Step 5: Detecting the True Range
Now we discover the actual boundaries of your calendar data:
let minStart = events[0].getStartTime();
let maxEnd = events[0].getEndTime();
events.forEach(event => {
const start = event.getStartTime();
const end = event.getEndTime();
if (start < minStart) minStart = start;
if (end > maxEnd) maxEnd = end;
});
Logger.log(`Detected calendar range: ${minStart} → ${maxEnd}`);
Logger.log(`Total events detected: ${events.length}`);
This loops through every event, tracking the earliest start time and latest end time. In my case, it detected a range from November 25th to December 16th with 157 events. Seeing this logged out gives you confidence that you're targeting the right calendar before making changes.
Step 6: The Time Shift (One Day in Milliseconds)
Google Calendar stores times as full datetime objects, so we'll perform this time travel using good old millisecond math:
const ONE_DAY_MS = 24 * 60 * 60 * 1000; // 86,400,000 milliseconds
24 hours × 60 minutes × 60 seconds × 1000 milliseconds = one full day. Boom shaka laka.
Step 7: Ready, Set, Shift
Next, we loop through each event, calculate new start and end times, and either log the change or execute it:
events.forEach(event => {
const oldStart = event.getStartTime();
const oldEnd = event.getEndTime();
const newStart = new Date(oldStart.getTime() - ONE_DAY_MS);
const newEnd = new Date(oldEnd.getTime() - ONE_DAY_MS);
if (DRY_RUN) {
Logger.log(`[DRY RUN] "${event.getTitle()}" ${oldStart} → ${newStart}`);
} else {
event.setTime(newStart, newEnd);
}
count++;
});
For each event:
- Grab the original start and end times
- Subtract one day (in milliseconds) from each
- Either log what would happen (dry run) or actually update the event
The setTime() method does the heavy lifting, updating both the start and end times in one call.
Step 8: The Victory Lap
After processing, the script logs a final summary:
Logger.log(`${DRY_RUN ? "Would shift" : "Shifted"} ${count} event(s) back by one day.`);
When we ran it for real, we watched as 157 events smoothly shifted back by exactly one day. No manual clicking. No copy-paste errors. Just clean, automated precision.
The Full Script
Here's the complete code, ready to copy into your Google Apps Script project:
function shiftCalendarEventsBackOneDay_AutoRange() {
const CALENDAR_ID = "[email protected]";
const DRY_RUN = true; // Set to false when ready to execute
const cal = CalendarApp.getCalendarById(CALENDAR_ID);
if (!cal) throw new Error("Calendar not found. Check CALENDAR_ID.");
const FAR_PAST = new Date(2000, 0, 1);
const FAR_FUTURE = new Date(2100, 0, 1);
const events = cal.getEvents(FAR_PAST, FAR_FUTURE);
if (events.length === 0) {
Logger.log("No events found in the calendar.");
return;
}
let minStart = events[0].getStartTime();
let maxEnd = events[0].getEndTime();
events.forEach(event => {
const start = event.getStartTime();
const end = event.getEndTime();
if (start < minStart) minStart = start;
if (end > maxEnd) maxEnd = end;
});
Logger.log(`Detected calendar range: ${minStart} → ${maxEnd}`);
Logger.log(`Total events detected: ${events.length}`);
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
let count = 0;
events.forEach(event => {
const oldStart = event.getStartTime();
const oldEnd = event.getEndTime();
const newStart = new Date(oldStart.getTime() - ONE_DAY_MS);
const newEnd = new Date(oldEnd.getTime() - ONE_DAY_MS);
if (DRY_RUN) {
Logger.log(`[DRY RUN] "${event.getTitle()}" ${oldStart} → ${newStart}`);
} else {
event.setTime(newStart, newEnd);
}
count++;
});
Logger.log(`${DRY_RUN ? "Would shift" : "Shifted"} ${count} event(s) back by one day.`);
}
Running the Script
- Paste the code into a new Google Apps Script project
- Replace
CALENDAR_IDwith your actual calendar ID - Keep
DRY_RUN = trueand click Run - Authorize the script when prompted (standard Google OAuth flow)
- Check the Execution Log to verify it's targeting the right events
- Set
DRY_RUN = falseand run again to execute the script and shift the dates
Automation For the Win! Again
What could have been an hour of tedious clicking became a 5-second script execution. Google Apps Script is a handy way to giving us direct access to Calendar's API without any external setup.
The real power here is adaptability. Need to shift events forward instead? Change the minus to a plus. Need to shift by a week? Multiply ONE_DAY_MS by 7. You can mix up the time intervals as needed.
The next time your bulk calendar import or project planning hacks throwsyou a curveball, remember: there's probably a script for that.
Have questions or want to see more automation scripts? Drop a comment below or reach out—I'm always down to nerd out about developer tooling.