DEV Community

Nomad Wozo
Nomad Wozo

Posted on AI-assisted

Why counting business days is harder than it looks

"Delivery in 10 business days." Sounds simple, right up until two people land on two different dates. Here are the traps I keep running into, plus a small JavaScript algorithm that doesn't drift by a day.

1. Does the start date count?

It's Monday and the deadline is "5 business days". Is that this Friday or next Monday? It depends on the convention:

  • Exclusive (the start date doesn't count): Monday + 5 business days = next Monday.
  • Inclusive (the start date is day 1): the 5th business day is Friday.

Excel uses both. WORKDAY returns the date a given number of working days after the start date, so the start date itself isn't counted. NETWORKDAYS counts whole working days including both ends. So NETWORKDAYS(start, WORKDAY(start, 5)) returns 6 when the start is a workday. Agree on the convention before you argue about the deadline.

2. Weekends aren't always Saturday and Sunday

A few examples:

  • Saudi Arabia: Friday–Saturday
  • Israel (Friday–Saturday for most offices; Sunday is a workday)
  • Retail, healthcare and shift teams often have a single day off, or a rotating one

Excel handles this with WORKDAY.INTL and NETWORKDAYS.INTL. The weekend argument takes a number (1 = Saturday–Sunday, 7 = Friday–Saturday, 11 = Sunday only) or a 7-character string that starts on Monday, where 1 means day off: "0000011" is Saturday–Sunday, "0000001" is Sunday only.

If your code hard-codes [0, 6], it's wrong for a lot of people.

3. Holidays move

  • Observed holidays. For US federal employees, a holiday that falls on a Saturday is observed on the Friday before, and one that falls on a Sunday is observed on the Monday after.
  • Substitute holidays. The UK and South Korea, among others, add a substitute day off when certain holidays land on a weekend.
  • Local holidays. National, state and city holidays stack. There is no single "holiday list" for a country that works for everyone.

The right holiday list is the one that applies to that team, in that place, this year.

4. Exceptions: a day off that becomes a workday

Make-up days, inventory Saturdays, on-call weekends... A usable model needs both extra days off and exceptional working days, and the exceptions have to override the weekly day off.

5. Time zones break dates

In JavaScript, new Date("2026-03-02") is parsed as midnight UTC. Anywhere west of UTC (the Americas, for example), getDay() and getDate() give you Sunday the 1st instead of Monday the 2nd. Parse date-only strings yourself and stay in UTC from start to finish.

An algorithm that handles all of the above

const parse = s => { const [y, m, d] = s.split("-").map(Number); return new Date(Date.UTC(y, m - 1, d)); };
const fmt = d => d.toISOString().slice(0, 10);

function isWorkday(date, { weekendDays = [0, 6], holidays = [], extraWorkdays = [] } = {}) {
  const key = fmt(date);
  if (extraWorkdays.includes(key)) return true;   // an exception beats the weekly day off
  if (holidays.includes(key)) return false;
  return !weekendDays.includes(date.getUTCDay()); // 0 = Sunday
}

// n > 0 moves forward, n < 0 moves backward.
// inclusive: the start date counts as day 1 if it is a workday.
function addWorkdays(start, n, { inclusive = false, ...cal } = {}) {
  const step = Math.sign(n) || 1;
  let left = Math.abs(n);
  const d = parse(start);
  if (left === 0) return fmt(d);
  if (inclusive && isWorkday(d, cal)) left--;
  while (left > 0) {
    d.setUTCDate(d.getUTCDate() + step);
    if (isWorkday(d, cal)) left--;
  }
  return fmt(d);
}

const cal = { holidays: ["2026-11-20"] };
addWorkdays("2026-11-16", 5, cal);                      // "2026-11-24"
addWorkdays("2026-11-16", 5, { ...cal, inclusive: true }); // "2026-11-23"
addWorkdays("2026-03-02", 5);                           // "2026-03-09"
addWorkdays("2026-03-02", 5, { inclusive: true });      // "2026-03-06"
Enter fullscreen mode Exit fullscreen mode

It walks one calendar day at a time instead of doing clever math with / 7. For deadlines measured in days or weeks, that's plenty fast, and it's much easier to explain why a given date was skipped.

In TypeScript, I'd type the calendar as { weekendDays: number[]; holidays: string[]; extraWorkdays: string[] } and use Sets if the lists get long.

Checklist

  1. Write down whether the start date counts.
  2. Make the weekend configurable.
  3. Let the user supply holidays; don't pretend one list fits a whole country.
  4. Support exceptional working days.
  5. Parse date-only strings in UTC.
  6. Show which days were counted and which were skipped.

How does your team count "business days"? I'd love to hear the edge cases I missed.


Disclosure: I build WorkdayMap, a free business-day calculator that applies these rules and shows every counted and skipped day.

Top comments (0)