Export Google Calendar to Excel and count where the hours went
The reason this search happens is almost always the same. Someone needs a number. How many hours went to meetings last month, how much of the week belongs to one client, whether the two hours a day that feel like they vanish actually vanish. A calendar shows a shape. A spreadsheet shows a total. So the obvious move is to export the calendar and open it in Excel.
Then the download arrives and it is a zip file full of .ics files, which Excel does not open. That is not a mistake in the steps. Google Calendar has no spreadsheet export, and the gap has to be closed on your side.
Google Calendar exports one format and imports two
Worth stating plainly, because it explains every dead end in this task. Google Calendar can only produce iCalendar files, ending in .ics. Exporting from Settings gives a zip containing one .ics per calendar. Exporting a single calendar gives one .ics file. There is no CSV option and no XLSX option anywhere in the interface.
The import side is different. Google documents that events can be imported from both .ics and .csv files on a computer. So Google Calendar reads spreadsheets but will not write one.
That asymmetry is why articles on this subject fall into two camps. The ones that say "export, then open in Excel" are describing the import direction and have the arrow backwards. The ones that get it right all involve a conversion step, and the only real question is where that step happens.
Three routes close the gap, and they suit different situations.
Decide what one row means before exporting anything
The step people skip is the cheapest one. A spreadsheet of calendar events can be built on three different units, and the unit decides which route works.
A row per event is the unit most conversions produce. It answers questions about variety: how many distinct commitments exist, how many have a location, which titles repeat. It is a poor unit for adding up time, because one row can represent a single half hour or a year of weekly standups.
A row per occurrence is the unit a timesheet needs. Every instance of a repeating meeting gets its own row with its own date, so a sum of durations is a real figure. Producing this unit means expanding recurrence rules, which is the single largest difference between the routes below.
A row per day or per week is the unit a report needs, and it is built from the occurrence rows by grouping them. Nothing exports at this grain, so it is always a second pass in the spreadsheet.
Writing down which of the three is wanted takes a minute and rules out at least one route immediately. Anyone whose answer is "hours per client per month" has just established that the recurrence problem is not optional.
Route one: export the file and convert it
This is the route with no dependencies, and it is the right one for a single month of data on a machine where installing nothing is the constraint.
Export the calendar first. On a computer, open Google Calendar, open Settings, click Import and export, and click Export under Export. A zip file downloads. Unpack it and you have one .ics file per calendar, named for the calendar address. If only one calendar matters, hover its name under My calendars, open Settings and sharing, and use Export calendar to get that file on its own.
Now the conversion. An .ics file is plain text, and each event is a block between BEGIN:VEVENT and END:VEVENT containing a summary, a start timestamp, an end timestamp, and often a location. Turning that into columns means reading those blocks and writing one row each. A short script, a text editor with regular expressions, or one of the many browser based converters will do it.
The conversion is also where the field names stop matching. A calendar file labels the title SUMMARY, the start DTSTART and the end DTEND, and a converter that passes those through verbatim leaves a sheet with headers nobody can read. Renaming them on the way in is trivial and saves every later formula from being written against a name that means nothing.
Two things to check before trusting the result. First, the timestamps. Calendar files often store times in UTC, which means a nine in the morning meeting can arrive as midnight and every daily total shifts. Second, recurring events. A weekly standup is stored once with a recurrence rule, not fifty-two times, so a naive conversion produces one row where the calendar shows a year of meetings. Any duration figure calculated from that file will be wrong, and wrong in a direction that flatters the schedule.
Route two: pull the events with a script into Sheets, then download as Excel
This is the route that scales, and it is the one worth learning if the question will be asked again next month.
Google Apps Script reaches Google Calendar through the CalendarApp class. The method that matters is getEvents(startTime, endTime), which returns every event in a window. On each event, getTitle(), getStartTime(), getEndTime(), getLocation(), getDescription() and getGuestList() give the fields a timesheet needs. The whole script is short:
function dumpMonth() {
const cal = CalendarApp.getDefaultCalendar();
const events = cal.getEvents(new Date('2026-09-01'), new Date('2026-10-01'));
const rows = events.map(e => [
e.getTitle(),
e.getStartTime(),
e.getEndTime(),
(e.getEndTime() - e.getStartTime()) / 3600000,
e.getLocation(),
e.getGuestList().length,
]);
const sheet = SpreadsheetApp.getActiveSheet();
sheet.appendRow(['Title', 'Start', 'End', 'Hours', 'Location', 'Guests']);
sheet.getRange(2, 1, rows.length, 6).setValues(rows);
}
The advantage over route one is that recurring events arrive expanded. getEvents returns the occurrences inside the window, so a weekly standup appears as four or five rows rather than one, and an hours total is a real total. The guest count column is the one that usually changes someone's mind, because it converts a meeting into a cost.
From there, Excel is a menu item. In Google Sheets, open File then Download and choose Microsoft Excel. The file opens in Excel with the rows intact and the formulas converted.
CalendarApp also has getAllCalendars() and getCalendarById(), so the same script can sweep several calendars and add a column naming which one each event came from. That single extra column is what turns a list of events into an answer about where the week goes.
Route three: an add-on from the Workspace Marketplace
Several add-ons in the Google Workspace Marketplace exist to put calendar events into a spreadsheet on a schedule, and they are a reasonable choice for anyone who does not want to maintain a script. The honest caveat is that pricing and free tiers on these tools change, so the number to check is the one on the listing today rather than one quoted in an article. What to compare is straightforward: which calendars the add-on can read, whether it expands recurring events, whether it can refresh on a timer, and what access it asks for on the account.
One more thing to weigh is where the data ends up. An add-on that writes to a spreadsheet on a schedule is creating a standing copy of the calendar in a second place, which is convenient for reporting and is one more file to think about when an account is closed or a laptop is handed back. A script that runs on demand leaves nothing behind between runs.
The access question deserves a pause. A tool that reads a calendar is reading who you met and where you were. On a work account, an administrator may have to approve it before it installs at all.
Which route fits which job
| Route | Setup | Recurring events | Refreshes | Good for |
|---|---|---|---|---|
Export .ics and convert |
None | Stored as a rule, needs expanding | No | One month, one time |
| Apps Script to Sheets | Twenty lines | Expanded by getEvents |
Yes, on a trigger | A number you need monthly |
| Marketplace add-on | Install and authorise | Depends on the tool | Usually yes | Recurring reports, no code |
The column set Google will accept back
Anyone moving hours into a spreadsheet often needs to move events the other way later, filling a calendar from a plan. Google documents the exact CSV format for that, and matching it saves a lot of guessing.
Only two headers are required. Subject is the event name, and Start Date is the first day, written in month, day, year order, as in 05/30/2020. The optional headers are Start Time, End Date, End Time, All Day Event, Description, Location and Private. Times look like 10:00 AM. All Day Event and Private take True or False.
Two rules trip people up. Google states that the headers must be in English, whatever language the account interface is set to. And any field containing a comma, which locations usually do, has to be wrapped in quotation marks or the columns shift.
One limit is worth planning around before building a workflow that leans on CSV. Google documents that repeating events imported from a .csv file might not appear as a series, and will instead land as a run of one-time events. Routing a recurring schedule through a spreadsheet therefore flattens it, and editing the series afterwards means editing every occurrence.
Also note what never survives the trip in either direction. Google states that when events are imported, guests and conference data are not imported. A spreadsheet round trip strips the invitees and the video link from every meeting.
What the totals will not tell you
A finished sheet answers the question that was asked, and the danger is treating it as the whole picture.
The clearest example is the meeting that appears twice. A shared invitation that also sits on a personal calendar produces two rows with the same title and the same hour, and a sum across calendars counts it twice. Deduplicating on title and start time fixes most of it, and the leftovers are usually the ones with slightly different titles.
Declined and tentative meetings still occupy rows unless the script filters on guest status, so a total built from raw event durations counts time that was never spent. Overlapping events double count, and anyone who books a focus block over a meeting they did not plan to attend will see more than twenty-four hours in a day.
The larger omission is everything between the events. Two meetings forty minutes apart across town read as two hours of meetings in a spreadsheet and consume half a day in reality. Calendars that account for travel time between appointments surface that cost while the schedule is being built rather than in a report a month later. The same goes for the ten minutes before a call and the twenty after it, which no export will ever show because nothing was ever booked.
A spreadsheet is good at telling you what the schedule contained. It cannot tell you what the schedule cost. That part comes from watching where the day actually breaks, which is a job for the calendar you work in, not the export.
What to change first
Pick the route by how often the question will come back. For a single answer, export the .ics, convert it, and be careful with recurring events and time zones. For a number you will want every month, spend twenty minutes on an Apps Script that writes to a sheet, add a guest count column, and download as Excel when someone asks. Then fix the thing the spreadsheet revealed, which is usually the gaps between meetings rather than the meetings. For a Mac calendar built around that gap, see Caltimate.
Frequently asked questions
Can Google Calendar export directly to .xlsx or .csv?
No. The only export format is .ics. Google Calendar reads .csv on import but never writes one, so every path to Excel involves a conversion step, either by processing the .ics file or by pulling events through a script or an add-on.
Why does the export download as a zip file?
The Settings export includes every calendar you can manage and packs one .ics file per calendar into a zip. Google's instructions say to open the zip and take the individual files out. Exporting a single calendar from its own settings page gives a plain .ics file instead.
Will recurring meetings be counted correctly?
Only if the method expands them. An .ics file stores a weekly meeting once with a recurrence rule, so a simple conversion produces one row instead of fifty-two. The Apps Script method returns the individual occurrences inside the date range, which is why hour totals from it are trustworthy.
Do the exported rows include who attended?
Not from a plain file export. Google documents that guests and conference data are not carried through an import, and a converted .ics file usually leaves attendees out. A script can read the guest list on each event, which is how a guest count column gets built.
Is there a way to keep the spreadsheet updated automatically?
Yes, with Apps Script or a Marketplace add-on. A script can run on a time driven trigger and rewrite the sheet on a schedule. A one-time export cannot refresh itself, so any figure taken from it is a snapshot of the moment it was downloaded.