Syncing Indian State Holidays to Google Calendar & Outlook: Dynamic iCal Feeds
Why default Google Calendar holiday feeds miss regional Indian state holidays, and how to dynamically generate RFC 5545 iCalendar feeds using the Calendar API.
1. The Missing Link in Google Calendar
Google Calendar provides a pre-packaged public holiday calendar for India (en.indian#holiday@group.v.calendar.google.com). While convenient, it exhibits severe operational limitations for distributed engineering teams:
- It bundles central holidays and broad religious festivals, but completely omits state-specific gazetted holidays.
- An employee in Bengaluru does not see Kannada Rajyotsava (Nov 1) or Ugadi.
- An employee in Mumbai does not see Maharashtra Day (May 1) or Ganesh Chaturthi.
- Teams relying on Google Calendar for project sprint planning, release freezes, or on-call rotations suffer scheduling conflicts due to untracked regional days off.
2. The iCalendar (RFC 5545) Standard
Modern calendar clients (Google Calendar, Apple Calendar, Microsoft Outlook, Thunderbird) support subscribing to web-hosted .ics calendar feeds via WebCal / HTTP.
An RFC 5545 compliant feed requires:
BEGIN:VCALENDARandVERSION:2.0headers.- Distinct
VEVENTblocks for every holiday event. - Deterministic
UIDgeneration (e.g.uid-${year}-${region}-${holiday_id}@calendar-api.org) so calendar clients update existing events rather than duplicating them. - All-day event timestamps using
VALUE=DATE:YYYYMMDD.
3. Generating Dynamic iCal Feeds from the Calendar API
By querying the Calendar API's /v1/holidays endpoint, you can serialize regional payloads into an on-demand .ics stream:
``bash # Fetch state holidays for Karnataka (KA) for year 2026 curl -s "https://calendar-api-d7a8.onrender.com/v1/holidays?country=IN®ion=KA&year=2026" ``
Each returned record contains the exact date, holiday name, and administrative classification (gazetted_holiday vs restricted_holiday).
4. Live iCal Feed Server (Node.js Edge Endpoint)
Here is an Express/Node.js endpoint that pipes state-filtered Indian holidays directly into Google Calendar or Apple Calendar:
import express from 'express';
import fetch from 'node-fetch';
const app = express();
app.get('/feeds/:region.ics', async (req, res) => {
const { region } = req.params;
const year = new Date().getFullYear();
const apiUrl = `https://calendar-api-d7a8.onrender.com/v1/holidays?country=IN®ion=${region.toUpperCase()}&year=${year}`;
const response = await fetch(apiUrl);
const payload = await response.json();
if (!payload.success) {
return res.status(404).send('Region or year holiday data not found');
}
let ics = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//India Calendar API//NONSGML Regional Holidays//EN',
`X-WR-CALNAME:India Holidays (${region.toUpperCase()})`,
'X-WR-TIMEZONE:Asia/Kolkata',
'CALSCALE:GREGORIAN'
];
for (const h of payload.data) {
const compactDate = h.date.replace(/-/g, '');
const uid = `${compactDate}-${region}-${h.name.replace(/\s+/g, '-').toLowerCase()}@calendar-api`;
ics.push(
'BEGIN:VEVENT',
`UID:${uid}`,
`DTSTAMP:${compactDate}T000000Z`,
`DTSTART;VALUE=DATE:${compactDate}`,
`SUMMARY:${h.name} (${h.type === 'gazetted_holiday' ? 'Gazetted' : 'Restricted'})`,
`DESCRIPTION:${h.description || 'Public holiday in ' + region}`,
'STATUS:CONFIRMED',
'TRANSP:TRANSPARENT',
'END:VEVENT'
);
}
ics.push('END:VCALENDAR');
res.setHeader('Content-Type', 'text/calendar; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="holidays-${region}.ics"`);
res.send(ics.join('\r\n'));
});
app.listen(3000, () => console.log('iCal feed generator active on port 3000'));