advent calendar, bags, advent season, christmas decoration, christmas, gifts, greeting card, surprise, pay, packaging, merry christmas, advent, advent calendar, advent calendar, advent calendar, advent calendar, advent calendar. Spreadsheet or API: where the calendar should actually live
Photo by congerdesign on Pixabay

Reviews

Spreadsheet or API: where the calendar should actually live

Spreadsheet or service: where the calendar lives, the contract a date must satisfy crossing between them, and one test set both sides should pass alike.

What to take away

A spreadsheet and a service both compute dates correctly. Where the calendar should actually live depends on who changes it and how the answer must be reproduced. The spreadsheet side includes Google Sheets, Microsoft Excel, Airtable, Smartsheet and Notion. The service side includes Google Calendar API, Microsoft Graph, Nager.Date, Workalendar and Calendarific.

The choice also depends on how a date travels between them without shifting by a day.

This page runs no calculation and recommends no product. It explains what each kind of tool is for and the contract a date must meet to cross between them. It also gives a request shape carrying everything a working-day service needs, plus one test set both tools should pass identically.

  • Spreadsheets such as Google Sheets, Microsoft Excel, Airtable, Smartsheet and Notion store a date as a count or text field. Services such as Google Calendar API, Microsoft Graph, Nager.Date, Workalendar and Calendarific return dates as text or typed values. Every conversion between them is a place to lose a day.
  • The closure list is the only part of the calculation with any judgment in it. Keep one copy, version it, and load the same version into both tools.
  • Run the same test cases through both. A difference is never a bug in the arithmetic; it is a convention one tool applied and the other did not.

What each tool is for

Spreadsheet

One person needs one date, now
Yes
Several people need the same kind of date
Fragile: each copy drifts
The date belongs to a record in another system
No
The answer must be reproduced months later
Only if the closure tab is versioned
The calendar changes often
Every workbook must be updated
Someone must be able to audit the working
Yes: the formula is visible

Service

One person needs one date, now
Overkill
Several people need the same kind of date
Yes: one calendar, one convention
The date belongs to a record in another system
Yes
The answer must be reproduced months later
Yes, if the calendar is versioned
The calendar changes often
One place to update
Someone must be able to audit the working
Only if the response echoes its inputs

The middle rows are where most organizations sit. Five people computing the same kind of deadline in five workbooks will not produce five identical calendars for long.

Spreadsheet vs service

Spreadsheet

One person, one date
Yes
Several people, same date
Copies drift
Date in another record
No
Reproduced months later
If versioned
Calendar changes often
Update each workbook
Audit the working
Formula visible

Service

One person, one date
Overkill
Several people, same date
One convention
Date in another record
Yes
Reproduced months later
If versioned
Calendar changes often
One place
Audit the working
If inputs echoed

Named tools make the split concrete. Google Sheets and Microsoft Excel keep the formula in the cell, so the working is visible. Airtable, Smartsheet and Notion store dates in a shared record, which helps when several people touch the same calendar.

Google Calendar API and Microsoft Graph tie dates to events and accounts. Nager.Date, Workalendar and Calendarific supply holiday data.

ToolTypeDate storageAudit and versioningTypical costFits
Google SheetsSpreadsheetSerial day count from 1899-12-30Cell formula visible; version historyFree with a Google accountOne person who needs a visible formula
Microsoft ExcelSpreadsheetSerial day count from 1900-01-01 on the default 1900 systemCell formula visible; workbook versionsMicrosoft 365 subscription or one-time licenseDesktop work and large workbooks
AirtableSpreadsheet-databaseDate fields stored as ISO 8601 textRevision history on paid plansFree plan; Team listed at $20 per user per month billed annuallyTeams that need linked records
SmartsheetWork managementDate columns typed as dates or textActivity log on paid plansPaid plans start around $9 per user per monthTeams with approvals and shared sheets
NotionDatabase and docsDate property stores ISO 8601Page historyFree for individuals; Plus $10 per member per month billed annuallyLightweight shared calendars
Google Calendar APIServiceRFC 3339 date or dateTimeGoogle Workspace audit logsFree with a Google account, with quota limitsDates tied to events and invites
Microsoft GraphServiceISO 8601 dateTimeTimeZoneMicrosoft 365 audit logsIncluded with Microsoft 365Microsoft 365 records
Nager.DateServiceYYYY-MM-DD holiday datesNo built-in audit trailFree, no API keyPublic holiday lookups
WorkalendarPython libraryPython date objectsCode version controlFree and open sourceLocal holiday calculation
CalendarificServiceISO 8601 datesAPI request logsFree tier with a request cap, then paid plansHoliday data through an API key

The contract a date has to satisfy

Both sides count from an origin, and the origins differ. A sheet counts days from a date near the end of 1899; a service that speaks in seconds usually counts from the start of 1970, which is Unix time.

Two origins, two types

Spreadsheet

Origin
1899-12-30
A date is
Integer 46388
A moment is
Fraction after it
Difference is
Integer days
Boundary risk
Serial read as number

Service

Origin
1970-01-01
A date is
Text 2027-03-01
A moment is
Text with offset
Difference is
Duration
Boundary risk
UTC shifts the day

Neither number means anything without its origin. That is the argument for sending text in ISO 8601 order instead of either.

In a spreadsheet a date is an integer counting days from an origin. A time is the fraction after it.

On the common 1900 system, 1 January 2027 is 46388 and 1 March 2027 is 46447. =B2-A2 works because subtracting two integers subtracts two dates. A cell showing a date and a cell showing 46447 hold the same value in different clothes.

In a service a date is text: 2027-03-01. A moment is text with a time and an offset: 2027-03-05T23:30:00-05:00. The two are different types, and a difference between two dates is an integer while a difference between two moments is a duration.

Crossing the boundary is where days go missing:

  • A moment sent where a date was expected. 2027-03-05T23:30:00-05:00 is 06 March at 04:30 in UTC. A service that reads the date part after converting to UTC has moved your Friday to Saturday.
  • A serial sent where text was expected. 46447 is a number to a service, not a date, and a lenient parser may read it as something else entirely.
  • A date read as a moment at local midnight, then shifted. Local midnight in one zone is the previous evening in another, and the date part changes with it.
  • Month numbering. Some programming interfaces count months from zero, so a date assembled from numbers can silently land a month early. Check the convention of the library you are using before you build a date from parts.

The rule that survives all of these: send date-only values as YYYY-MM-DD text, never as a moment and never as a serial, and convert to a serial only inside the spreadsheet with DATEVALUE or DATE.

A request that carries everything

Google Calendar API and Microsoft Graph both accept ISO 8601 values. Nager.Date and Calendarific return holiday dates as text. Workalendar computes them inside a Python process. A request that carries its assumptions lets any of them be swapped without changing the answer.

Request that carries its assumptions

  1. Send operation, start, count or end
  2. Send weekend string and calendar version
  3. Send start_counts and end_counts
  4. Receive result and skipped dates
  5. Receive applied block echoing inputs
  6. Recheck the arithmetic months later

A working-day service that cannot be argued with takes all of its assumptions as inputs and returns them with the answer. Vendor-neutral shape:

request
  operation:        add | subtract | between | hours
  start:            2027-03-01
  count or end:     10                (or an end date, for between)
  weekend:          "0000011"         (Monday through Sunday, 1 = rest day)
  calendar:         { id: "court-x", version: "2026-09" }
  start_counts:     false
  end_counts:       true
  zone:             "Europe/Paris"    (for hour operations only)

response
  result:           2027-03-15
  skipped:          [ ...the dates that were passed over... ]
  applied:          { weekend, calendar, start_counts, end_counts }
  computed_at:      2026-09-10T08:12:44Z

The applied block is what makes the answer defensible. Six months later the question is usually what it was computed with, and a response that echoes its inputs answers that in one line. The skipped list is what lets a human check the arithmetic in seconds.

The spreadsheet equivalent of this shape is a row with the same columns and the formula in the last one. If the row does not carry the weekend string and the calendar version, it is a result with no working.

One calendar, both tools

The closure list is a table with three columns: calendar id, date, and a note naming the publisher and date. Keep exactly one such table.

The spreadsheet loads it as a named range. The service loads it from the same file or source. Give each revision a version, and put that version beside every computed date in both tools.

No calendar is supplied here. Which publisher's list governs a deadline, and why the answer is a body rather than a country, is under adding business days; hour inputs for hours are under working hours.

The same tests in both tools

Run these through the spreadsheet formula and the service, with the same weekend string and no closures except where stated. All dates are in March 2027. Any difference is a convention to reconcile, not a defect to fix. The service can be Google Calendar API, Microsoft Graph, Nager.Date, Workalendar or Calendarific.

Nine cases both tools must pass

  • Add 1 to Friday 5thMonday 8th
  • Add 1 to Saturday 6thMonday 8th
  • Add 0 to Saturday 6thunchanged
  • Add 5 to Monday 1stMonday 8th
  • Subtract 5 from Monday 8thMonday 1st
  • Count 1st to 8th, both ends6
  • Add 10 with 10th closedTuesday 16th
CaseExpected
Add 1 to Friday 5thMonday 8th
Add 1 to Saturday 6thMonday 8th
Add 0 to Saturday 6thSaturday 6th, unchanged
Add 5 to Monday 1stMonday 8th
Subtract 5 from Monday 8thMonday 1st
Subtract 1 from Sunday 7thFriday 5th
Count working days Monday 1st to Monday 8th, both ends in6
Add 10 to Monday 1st with Wednesday 10th closedTuesday 16th
Count Wednesday 10th to Wednesday 10th with the 10th closed0

The seventh row is the one that reconciles most disputes. A step of five working days and a set of six working days describe the same span, and a tool that reports the step as five while another reports the set as six has not disagreed about anything.

When to move from the sheet to the service

Move when more than one person computes the same kind of date, or the date must land in another system's record. Move when someone can challenge a date and get the working back, or the calendar changes more often than every workbook updates.

Until then the spreadsheet is the better tool. Its working is visible, and the tests above take a minute to run. Google Sheets and Microsoft Excel cover most single-team cases. Airtable, Smartsheet and Notion add shared records.

Forward and backward counts are different operations. Each fails in its own way. The backward count, where a wrong closure costs a day you thought you had, is under subtracting business days.

Both tools must agree on endpoint conventions. Those are under counting between two dates.

Common questions

Can I call a service from a spreadsheet cell?

Usually, and it is often the worst of both: a formula that depends on a network call, with the calendar in one place and the working in another. Google Sheets can call APIs through Apps Script; Excel can through Power Query or Office Scripts.

If you do it, have the cell store the applied block alongside the result, rather than the date alone.

Should the service or the client decide the convention?

The request should state it and the response should echo it. A service with a silent default is a service whose answers cannot be compared with anything else.

How do I version a closure list?

A date stamp on the file and a version column in the table. Every computed date carries the version it used. When the list changes, old dates keep their old version and are not recomputed unless someone decides to.

Is a moment ever the right thing to send for a date?

Only for hour operations, where the instant matters. For day operations, a moment is a date with an extra way to be wrong, as monthly working-day counts show when a month boundary sits at local midnight.

In this guide

  1. Spreadsheet and API tools formula testing for paritySpreadsheet and API tools formula parity: Excel NETWORKDAYS against Nager.Date, four pinned conventions, sixteen boundary cases, and the 1900/1904 epoch trap.
  2. Excel date serials and the 1900 and 1904 epochs: spreadsheet and API date handlingExcel date serials, the 1900 and 1904 epochs, text date traps, and spreadsheet and API tools that move dates without shifting or corrupting them.
  3. Spreadsheet and API Tools: Google Sheets Date SettingsSpreadsheet and API tools in Google Sheets can reinterpret dates through file time zone and locale settings, serial values, and API response formatting.
  4. Spreadsheet and API tools holidays 2027: criteria, no datesSpreadsheet and API tools holidays 2027: criteria for one closure list in Excel, Sheets, and Nager.Date, versioning it, and why no dates are listed here.
  5. Spreadsheet Date and Workday Counting MistakesSpreadsheet date and workday-count mistakes: text dates, locale-dependent imports, midnight shifts, serial values, endpoint rules, and closure ranges.

More in Reviews

Latest from Review Desk