DEV Community

Cover image for Auto-Sync Jewish & Christian Holidays in Google Calendar with Apps Script
Syed Bilal Hussain
Syed Bilal Hussain

Posted on

Auto-Sync Jewish & Christian Holidays in Google Calendar with Apps Script

I built an Apps Script add-on that imports Jewish and Christian holidays directly into a user’s main Google Calendar, with duplicate protection, one-day-before reminders, color coding, and an annual auto-refresh. No external backend, no JSON feed — just two official Google holiday calendars, user selections saved in PropertiesService, and pure Apps Script UI.

This article explains what the add-on does, why the exact implementation matters, and the key code decisions you’ll want to copy.

The concrete problem

Subscriptions and ICS imports fail in predictable ways:

  • ICS imports create duplicates or orphaned events.
  • Users forget to re-import for the next year.
  • Many “holiday feeds” are regional or incomplete.
  • Calendar add-ons are awkward because CardService is under-documented.

I needed a small, reliable tool that copies relevant holiday events into the user’s primary calendar and keeps them up to date each year.

What this add-on actually does (precisely)

Sources: reads events from two Google-managed holiday calendars:

Filter: matches event titles against a built-in defaultHolidays array or the user’s saved selection.

Target: inserts events directly into the user’s main calendar (Session.getActiveUser().getEmail()).

Duplicate protection: checks existing events on the same day for identical titles before adding.

Reminders: creates a pop-up reminder on the previous day at the user-selected hour (default 21:00).

Colors: sets color IDs per source calendar.

Auto-refresh: deletes old imported holidays and re-imports up to one year forward; runs via a yearly trigger (guarded by yearlyHolidayCheck()).

No external data files: everything needed is in script files and PropertiesService.

Key code snippets (behavioral highlights)

Source calendars and target calendar

const jewishCalendarId = "en.judaism#holiday@group.v.calendar.google.com";
const christianCalendarId = "en.christian#holiday@group.v.calendar.google.com";
const targetCalendarId = Session.getActiveUser().getEmail();

Which holidays are considered

Hard-coded fallback list:

const defaultHolidays = [
"Christmas","Good Friday","Easter","Pentecost",
"Palm Sunday","Ascension","Assumption","All Saints",
"Rosh Hashanah","Yom Kippur","Sukkot",
"Passover","Shavuot","Hanukkah"
];

Or user-selected list stored in SELECTED_HOLIDAYS via PropertiesService.

Duplicate-safe import (title match)

const existing = targetCal.getEventsForDay(startTime).find(e => e.getTitle() === title);
if (existing) continue;

One day before the pop-up reminder at the chosen hour

const reminderTime = new Date(startTime);
reminderTime.setDate(reminderTime.getDate() - 1);
reminderTime.setHours(reminderHour, 0, 0, 0);
const minutesBefore = (startTime - reminderTime) / (1000 * 60);
newEvent.addPopupReminder(minutesBefore);

Annual refresh and email confirmation

autoRefreshHolidays() calls deleteHolidayEvents() then syncHolidayEvents() and emails the user a completion note. The trigger is created with:

ScriptApp.newTrigger("yearlyHolidayCheck")
.timeBased()
.onMonthDay(1)
.atHour(0)
.create();

yearlyHolidayCheck() includes a guard that verifies the date is Jan 1 before running.

UI and settings

  • CardService (UI.gs) provides the add-on Homepage and onboarding flow inside Calendar.
  • An HTML wizard (Onboarding.html) and a Sidebar.html provide an alternate modal/sidebar interface for initial setup and settings.
  • Settings and user selections are saved to PropertiesService (no external storage).

Resources

If you want the full production-ready version of this add-on (complete UI flow, trigger setup, onboarding wizard, and settings sidebar), I’ve packaged the template here.

→ Get it here

Top comments (0)