A while back I needed to know how many working days I had left before a deadline. My first instinct was: total days divided by 7, times 5, done. That works fine as long as your date range happens to be a whole number of weeks. The moment you have leftover days at either end — which is basically always — that formula falls apart, because whether a leftover day “counts” depends entirely on which day of the week it lands on. I ended up building a small workday calculator just to get this right, and the actual logic turned out to be less “math” and more “walk the calendar and check.”
Full weeks are cheap, partial weeks aren’t
The calculator lets you define your own work week (a set of weekday numbers, 0=Sunday through 6=Saturday, defaulting to Mon–Fri) instead of assuming a fixed 5-day week. That’s the part that breaks the naive division trick — you can’t just multiply by 5 if the user’s work week has 4 days or includes Saturday.
Here’s the “count workdays between two dates” function, close to what’s actually running:
let start = DateTime.fromISO(twoDate.date1);
let end = DateTime.fromISO(twoDate.date2);
let source_Day = end.diff(start, "days").toObject().days;
let res_week = Math.floor(Math.abs(source_Day) / 7);
let firstDate = source_Day > -1 ? twoDate.date1 : twoDate.date2;
let lastDate = source_Day > -1 ? twoDate.date2 : twoDate.date1;
let firstDate_weekday = new Date(firstDate).getDay();
let lastDate_weekday = new Date(lastDate).getDay();
let resDays = res_week * myWorkday.value.length;
let tempEnd =
lastDate_weekday >= firstDate_weekday
? lastDate_weekday
: lastDate_weekday + 7;
for (let i = firstDate_weekday; i <= tempEnd; i++) {
if (myWorkday.value.includes((i % 7).toString())) {
resDays++;
}
}
Enter fullscreen mode Exit fullscreen mode
The trick is splitting the range into two very different problems:
-
Complete 7-day blocks.
res_week = floor(totalDays / 7). Any full 7-day span always contains exactly one occurrence of every weekday, so it always contains exactlymyWorkday.lengthworkdays — no matter which day it starts on. That’s why this part can be pure multiplication. -
The leftover partial week. This can’t be reduced to a day count, because whether those leftover days count depends on which weekdays they actually are. So instead of computing “how many leftover days,” the code computes the leftover’s start and end weekdays, and walks weekday-by-weekday from one to the other (
tempEndbumps by 7 if the end weekday number is smaller than the start, to handle wrapping across a week boundary, e.g. Friday → Monday), checking each one against the custom workday set.
Both the start date and end date are included in the count — that’s an explicit design choice in the tool, not an accident of the math.
Adding N workdays to a date has the same problem, mirrored
The other half of the tool answers a different question: “what date is 10 workdays from now?” Same underlying issue — you can’t just add 10 * 7/5 calendar days, because it matters which weekday you start counting from:
let infer_Week = Math.floor(inferNumber.value / myWorkday.value.length);
let infer_remainder = inferNumber.value % myWorkday.value.length;
if (infer_Week > 0) {
infer_Week = infer_Week - 1;
infer_remainder = infer_remainder + myWorkday.value.length;
}
let start_weekday = new Date(singleDate.date).getDay();
let nowWeekday = dateDirection.value == 0
? (start_weekday + 1) % 7
: (start_weekday + 6) % 7;
let inferDays = infer_Week * 7;
let i = infer_remainder;
while (i > 0) {
if (myWorkday.value.includes(nowWeekday.toString())) {
i--;
}
inferDays++;
nowWeekday = dateDirection.value == 0 ? (nowWeekday + 1) % 7 : (nowWeekday + 6) % 7;
}
Enter fullscreen mode Exit fullscreen mode
Same split as before: infer_Week full weeks get resolved by multiplication, and the leftover (infer_remainder) gets resolved by actually walking day-by-day and checking membership in the workday set, incrementing inferDays every calendar day but only decrementing the counter i on days that are actual workdays.
The one non-obvious line is the if (infer_Week > 0) block, which “borrows back” one full week into the remainder. Without it, a small remainder (say, needing 2 more workdays) combined with an unlucky starting weekday could theoretically need to walk further than the remainder value allows before hitting enough workdays. Padding the remainder with one extra full week’s worth of days guarantees the while loop always has enough calendar days to walk through to satisfy the count, regardless of which weekday you started on.
Note also that the start date itself is not counted here — the loop deliberately starts from start_weekday + 1 (or - 1 going backwards), so “5 workdays from Monday” lands on the following Monday, not the Friday four days later.
Where this actually breaks
A few real gotchas I ran into:
-
The weekday lookup uses
new Date(dateString).getDay(), not the same date library used everywhere else. The rest of the tool parses dates with Luxon’sDateTime.fromISO, but the weekday check falls back to plainnew Date("2024-01-01").getDay(). A bareYYYY-MM-DDstring is parsed by JS as UTC midnight, while.getDay()reads it back in the browser’s local time zone. For anyone west of UTC, that mismatch can roll the date back to the previous day right at midnight-ish boundaries and return the wrong weekday — which then throws off which days count as workdays for that edge of the range. - No public holidays. This only excludes the weekdays you tell it to exclude. If a national holiday falls inside your range, you have to manually adjust the result yourself — the tool doesn’t know your country’s calendar and doesn’t try to.
-
Deselecting every workday checkbox isn’t guarded against. Nothing stops you from unchecking all seven days. In the “between two dates” mode that just quietly returns 0. In the “add/subtract workdays” mode it’s worse:
myWorkday.value.lengthbecomes the divisor ininfer_Week = Math.floor(N / 0), which producesInfinity/NaN, and the result is a broken date instead of a warning. - The two modes disagree on whether the start date counts, and that’s intentional rather than a bug — “days between” includes both endpoints, “add N workdays” excludes the starting point — but it’s easy to get bitten by the asymmetry if you assume they behave the same way.
I turned the cleaned-up version of this into a small free tool if you want to poke at it: Business Day Calculator. No sign-up, and you can set your own work week instead of assuming Mon–Fri.
Available in other languages
- 工作天計算機 — 繁體中文
- 工作日计算器 — 简体中文
- Business Day Calculator — English
- 営業日計算機 — 日本語
- 근무일 계산기 — 한국어
- Calculateur de Jours Ouvrables — Français
- Калькулятор рабочих дней — Русский
- Arbeitstage-Rechner — Deutsch
- Kalkulator Hari Kerja — Bahasa Indonesia
- Calculadora de días laborables — Español
- Máy tính ngày làm việc — Tiếng Việt
- เครื่องคิดวันทำงาน — ไทย
- Kalkulator dni roboczych — Polski
- İş Günü Hesaplayıcı — Türkçe
- Calcolatore Giorni Lavorativi — Italiano
- Calculadora de dias úteis — Português
- Werkdagen Calculator — Nederlands
- Калькулятор робочих днів — Українська
답글 남기기