Blog / How to Convert an ICS File to JSON
· 4 min read

How to Convert an ICS File to JSON

Parse iCalendar into JSON for scripts, dashboards, and APIs — the field mapping that matters, and the parsing traps that produce wrong dates.

iCalendar is a line-based text format from 2009 with folding rules, escaped separators, and a recurrence grammar of its own. JSON is what the rest of your stack speaks. Here is how to get from one to the other without producing subtly wrong dates.

Do Not Write the Parser Yourself

iCalendar looks trivially parseable — colon-separated key-value pairs — and it is not. A correct parser has to handle line folding at octet boundaries, escaped commas and semicolons inside TEXT values, parameters with quoted values, and VTIMEZONE blocks that define their own DST transitions. Every one of these appears in real files from Google Calendar and Outlook.

Use a library:

  • JavaScript / TypeScriptical.js (the reference implementation, used by Thunderbird) or node-ical for a simpler API.
  • Pythonicalendar for parsing, plus recurring-ical-events if you need occurrences expanded.
  • Goarran4/golang-ical.

A Sensible Target Shape

{
  "calendarName": "Work",
  "events": [
    {
      "uid": "[email protected]",
      "title": "Team Standup",
      "start": "2026-04-15T09:00:00Z",
      "end": "2026-04-15T09:30:00Z",
      "allDay": false,
      "location": "Room 4",
      "description": "Daily sync",
      "recurring": true,
      "attendees": ["[email protected]"]
    }
  ]
}

Emit timestamps as ISO 8601 in UTC. Resist the temptation to keep local times with a separate timezone field unless you genuinely need to re-render the original wall-clock time — it doubles the number of ways downstream code can get it wrong.

The Four Traps

Recurring events

A single VEVENT with RRULE:FREQ=WEEKLY represents potentially hundreds of occurrences. Decide explicitly whether your JSON contains the rule or the expanded occurrences. Dashboards and spreadsheets want occurrences; a system that will re-render the calendar wants the rule. Expanding requires a bounded window — an RRULE with no UNTIL or COUNT is infinite.

All-day events

These use a DATE value with no time component, and the end date is exclusive. Serialising them as midnight UTC timestamps will shift them by a day for anyone east or west of UTC — the classic "my all-day event shows on the wrong date" bug.

Floating times

A DTSTART with no Z suffix and no TZID is a floating time, meaning "9am wherever the reader is". Converting it to UTC requires picking a timezone, and picking the server's is almost always wrong.

Exceptions to recurrence

EXDATE removes an occurrence and RECURRENCE-ID overrides one. Skipping these produces a calendar showing meetings that were cancelled or moved months ago.

Checking Your Output

Before trusting a parser on a large feed, open the same file in ICS Viewer and compare. Mismatches show up fastest around recurring events and anything near a DST boundary — if your event count differs from the rendered calendar, the recurrence handling is where to look.

Related

Want to preview an ICS file right now?

Open ICS Viewer →