Spyridon Pikoulas

How to export Google Calendar™ events to Google Sheets™

People export a calendar to a spreadsheet for timesheets, invoices and reports: one row per meeting with its date, times and hours. Google Calendar™ has no direct way to do this. Here is what it does have, and two ways that work.

1. Calendar’s own export (not a spreadsheet)

In Google Calendar™, Settings > Import & export > Export downloads a ZIP file of your calendars as .ics files. That’s a calendar format, meant for another calendar app. Google Sheets™ can’t open it as a table, and it holds each calendar’s whole history, with no date range. It works as a backup, not as an export to a sheet.

2. A short Apps Script (free)

Apps Script can read a calendar directly. This script adds a menu that writes a calendar’s events between two dates into a sheet of their own. Open a spreadsheet, go to Extensions > Apps Script, replace everything in Code.gs with the code below, and set the three lines at the top:

var CALENDAR = '';  // a calendar's name, or '' for your main calendar
var FIRST_DAY = '2026-10-01';
var LAST_DAY = '2026-10-31';

function onOpen() {
  SpreadsheetApp.getUi().createMenu('Calendar')
    .addItem('Export events', 'exportEvents')
    .addToUi();
}

/** The calendar's events from FIRST_DAY to LAST_DAY into a sheet of their own. */
function exportEvents() {
  var ss = SpreadsheetApp.getActive();
  var zone = ss.getSpreadsheetTimeZone();
  var cal = CALENDAR ? CalendarApp.getCalendarsByName(CALENDAR)[0] : CalendarApp.getDefaultCalendar();
  if (!cal) throw new Error('No calendar named ' + CALENDAR);
  var from = Utilities.parseDate(FIRST_DAY, zone, 'yyyy-MM-dd');
  var to = Utilities.parseDate(LAST_DAY + ' 23:59:59', zone, 'yyyy-MM-dd HH:mm:ss');
  // All-day dates come in the script's time zone: keep them as the date they are.
  var day = function (d) { return Utilities.formatDate(d, Session.getScriptTimeZone(), 'yyyy-MM-dd'); };
  var rows = cal.getEvents(from, to).map(function (e) {
    var allDay = e.isAllDayEvent();
    return [
      e.getTitle(),
      allDay ? day(e.getAllDayStartDate()) : e.getStartTime(),
      allDay ? day(new Date(e.getAllDayEndDate().getTime() - 1)) : e.getEndTime(),
      allDay ? '' : (e.getEndTime() - e.getStartTime()) / 3600000,
      e.getLocation(),
      e.getDescription(),
      e.getGuestList().map(function (g) { return g.getEmail(); }).join(', '),
    ];
  });
  var name = cal.getName() + ' ' + FIRST_DAY + ' to ' + LAST_DAY;
  var sheet = ss.getSheetByName(name) || ss.insertSheet(name);
  sheet.clear();
  var header = ['Title', 'Start', 'End', 'Hours', 'Location', 'Description', 'Guests'];
  sheet.getRange(1, 1, rows.length + 1, header.length).setValues([header].concat(rows));
  if (rows.length) sheet.getRange(2, 2, rows.length, 2).setNumberFormats(rows.map(function (r) {
    var f = r[3] === '' ? 'yyyy-mm-dd' : 'yyyy-mm-dd hh:mm';  // all-day: no time
    return [f, f];
  }));
  sheet.setFrozenRows(1);
}

Save, reload the spreadsheet, and choose Calendar > Export events. The first run asks you to authorize the script. It’s your own script, so if Google warns that it hasn’t verified the app, click Advanced > Go to (your project name) (unsafe).

The sheet it makes is named after the calendar and the dates, with one row per event:

CALENDAR takes the name as it appears in your calendar list, so it works for calendars shared with you too.

3. EventFill (free for 50 events a day)

EventFill is the add-on I make for this, if you’d rather not keep a script. Go to Extensions > EventFill > Open EventFill and open the Calendar to Sheet tab. Pick a calendar, the first and last day, and, if you like, words the events must contain. Then click “Import to a new sheet”.

It reaches only the spreadsheet you open it in and your calendars’ events, never your calendars’ settings or your other files. 50 events a day are free, every day. Past that, EventFill Pro is $9 once per Google account for new purchases, with no subscription or automatic renewal. Google service limits still apply; existing subscriptions and trials keep their billing terms.

Which one

For one export now and then, the script does the job. If you’d like the words filter, the columns ready for a report, or changes sent back to the calendar, try EventFill.