EDATE, EOMONTH, WORKDAY, NETWORKDAYS — Business Date Calculations with Spreadsheet Formulas in C#

· unvell team
EDATE, EOMONTH, WORKDAY, NETWORKDAYS — Business Date Calculations with Spreadsheet Formulas in C#

Business applications are full of date rules: “payment is due at the end of the month after the invoice date,” “ship within 5 business days,” “send a reminder 3 business days before the deadline.” Every one of them sounds trivial — and every one of them is fiddly to hand-code with DateTime. Months have different lengths, weekends get in the way, and public holidays ruin whatever loop you wrote last time.

Spreadsheets solved this decades ago with four functions: EDATE, EOMONTH, WORKDAY, and NETWORKDAYS. This article sorts out what each one does and the off-by-one traps they hide, and shows how ReoGrid (supported in V4.5) runs the same formulas right inside a C# app — so the date rules your users already know from Excel work unchanged in your WinForms / WPF screens.

This article is part of a series on running Excel’s workhorse functions in .NET. See also VLOOKUP vs HLOOKUP vs XLOOKUP and MATCH vs XMATCH.


The four functions at a glance

What it answersSignature
EDATE”The same day, N months later”EDATE(start, months)
EOMONTH”The last day of the month, N months later”EOMONTH(start, months)
WORKDAY”The date N business days later”WORKDAY(start, days, [holidays])
NETWORKDAYSHow many business days between two dates”NETWORKDAYS(start, end, [holidays])

The first two think in months, the last two think in business days (weekends and, optionally, holidays are skipped). Between them they cover most date rules a business app ever needs.


EDATE — the same day, N months later

EDATE(start, months) shifts a date by whole months, keeping the day of the month. Contract renewals, subscription billing, installment schedules:

=EDATE("2026/07/07", 1)     → 2026/08/07  (next billing date)
=EDATE("2026/07/07", 12)    → 2027/07/07  (one-year renewal)
=EDATE("2026/07/07", -6)    → 2026/01/07  (negative goes backward)

One behavior to know: if the target month is shorter, the day is clamped to the last day of that month — exactly like Excel:

=EDATE("2026/01/31", 1)     → 2026/02/28  (there is no Feb 31)

That clamp is usually what you want (“bill on the 31st, or the last day if the month is shorter”), but it means EDATE is not always reversible: going +1 then −1 month from Jan 31 lands on Jan 28.


EOMONTH — the invoice function

EOMONTH(start, months) returns the last day of the month N months away. This single function encodes the most common payment term in the world — “due at the end of the month following the invoice date”:

=EOMONTH(A2, 0)    → last day of the invoice month (the closing date)
=EOMONTH(A2, 1)    → end of the following month  ← classic "EOM + 1" terms
=EOMONTH(A2, 2)    → end of the month after that

Two idioms worth memorizing:

=EOMONTH(A2, -1) + 1        → the FIRST day of A2's month
=DAY(EOMONTH(A2, 0))        → how many days A2's month has

If you have ever written the new DateTime(y, m, DateTime.DaysInMonth(y, m)) dance in C#, EOMONTH is that dance as one word — and your users can read it.


WORKDAY — a deadline N business days away

WORKDAY(start, days, [holidays]) walks forward days business days, skipping Saturdays, Sundays, and anything in the optional holiday list:

=WORKDAY(A2, 5)             → 5 business days after A2 (shipping deadline)
=WORKDAY(A2, 5, H2:H20)     → same, also skipping the holidays in H2:H20
=WORKDAY(D2, -3, H2:H20)    → 3 business days BEFORE D2 (reminder date)

Two rules that catch everyone once:

  • The start date itself is not counted. WORKDAY("Mon", 1) is Tuesday, not Monday. “Within 5 business days, counting today” is WORKDAY(A2, 4).
  • If the start date falls on a weekend, counting simply begins from there — the first step lands on the next business day.

The negative-days form is quietly one of the most useful formulas in operations software: “send the reminder 3 business days before the due date” is just WORKDAY(due, -3, holidays).


NETWORKDAYS — how many business days in between

NETWORKDAYS(start, end, [holidays]) counts business days between two dates — inclusive of both endpoints:

=NETWORKDAYS("2026/07/06", "2026/07/10")    → 5   (Mon–Fri, all business days)
=NETWORKDAYS(A2, TODAY(), H2:H20)           → business days elapsed since A2
=NETWORKDAYS(EOMONTH(A2,-1)+1, EOMONTH(A2,0))  → working days in A2's month

Typical uses: SLA elapsed-time tracking (“this ticket has been open 4 business days”), payroll (“21 working days this month”), and progress dashboards. If start is later than end, the result is negative, matching Excel.

Note the asymmetry with WORKDAY: NETWORKDAYS includes the start date, WORKDAY excludes it. The pair NETWORKDAYS(A, WORKDAY(A, n)) = n + 1, not n — a classic source of off-by-one bugs in hand-rolled tests.


The holiday list — one range, shared by every formula

Both business-day functions take an optional third argument: a range of dates to treat as non-working. Put your public holidays (and company closures) in a column — on a hidden sheet if you like — and reference it everywhere:

H2:  2026/01/01     New Year's Day
H3:  2026/07/03     Independence Day (observed)
H4:  2026/11/26     Thanksgiving
...

=WORKDAY(A2, 5, Holidays!H2:H30)
=NETWORKDAYS(A2, B2, Holidays!H2:H30)

Maintaining that one range is your holiday calendar. No recompile when next year’s holidays are announced — exactly the kind of thing you want to keep as data, not code.

One current limitation to be aware of: weekends are fixed to Saturday and Sunday. The WORKDAY.INTL / NETWORKDAYS.INTL variants with custom weekend patterns are not included as of V4.5 — if your business week is Sunday–Thursday, model the difference through the holiday list.


Running these formulas inside a C# app

ReoGrid added all four functions in V4.5, alongside DATE, WEEKDAY, WEEKNUM, and the rest of the date batch. The same formulas users write in Excel calculate identically inside a WinForms / WPF spreadsheet control — no Office, no Interop.

Here’s a compact invoice due-date sheet:

using unvell.ReoGrid;
using unvell.ReoGrid.DataFormat;

var sheet = reoGridControl.CurrentWorksheet;

// Header
sheet["A1"] = new object[,] {
    { "Invoice date", "Amount", "Due (EOM+1)", "Remind on", "Days left" }
};

// Holiday list lives in its own column (or a hidden sheet)
sheet["H1"] = "Holidays";
sheet["H2"] = new DateTime(2026, 7, 3);
sheet["H3"] = new DateTime(2026, 9, 7);

// One invoice row
sheet["A2"] = new DateTime(2026, 7, 7);
sheet["B2"] = 1280.00;

// Due at the end of the month after the invoice date
sheet["C2"] = "=EOMONTH(A2, 1)";

// Reminder: 3 business days before the due date, skipping holidays
sheet["D2"] = "=WORKDAY(C2, -3, H2:H10)";

// Business days remaining until the due date
sheet["E2"] = "=NETWORKDAYS(TODAY(), C2, H2:H10)";

One thing you must do: format the result cells as dates

Like Excel, date functions return a serial number (the OLE Automation date). Leave the cell unformatted and EOMONTH shows up as 46265 instead of a date. Give the result columns a date format once:

sheet.SetRangeDataFormat("A2:A100", CellDataFormatFlag.DateTime,
    new DateTimeDataFormatter.DateTimeFormatArgs { Format = "yyyy/MM/dd" });
sheet.SetRangeDataFormat("C2:D100", CellDataFormatFlag.DateTime,
    new DateTimeDataFormatter.DateTimeFormatArgs { Format = "yyyy/MM/dd" });

And to pull a computed date back out of the sheet into your C# code, convert the serial with DateTime.FromOADate:

double serial = sheet.GetCellData<double>("C2");
DateTime dueDate = DateTime.FromOADate(serial);   // 2026/08/31

If a user clicks into C2 and edits the formula — changes 1 to 2 because that customer pays at the end of the month after next — everything downstream recalculates, exactly as it would in Excel. That is the point of putting a real formula engine in the UI instead of burying the date rules in C#: the payment terms become visible, editable data.


Recap — the traps in one list

  • EDATE / EOMONTH think in months; WORKDAY / NETWORKDAYS think in business days
  • EDATE clamps to month-end when the target month is shorter (Jan 31 + 1 month = Feb 28)
  • WORKDAY excludes the start date; NETWORKDAYS includes both endpoints — don’t mix them up in tests
  • Both business-day functions accept a holiday range as the third argument; maintain holidays as data, not code
  • Weekends are fixed Sat–Sun (the .INTL variants are not in V4.5)
  • Results are date serial numbers — set a DateTime format on result cells, and use DateTime.FromOADate when reading them from C#
  • ReoGrid V4.5 runs all of these inside a WinForms / WPF app. No Office required

See what’s new in ReoGrid 4.5 / Try the 30-day trial


Further reading

Try ReoGrid in your own project

The Excel-compatible spreadsheet component for .NET WinForms and WPF. 30-day free trial — no credit card required.

Related articles