Calcular días hábiles entre dos fechas parece un problema resuelto. Hay decenas de snippets en Stack Overflow. Pero la mayoría falla en producción por razones que no son obvias hasta que el error ya llegó a un cliente.
El snippet básico que todos usan
function countBusinessDays(startDate, endDate) {
let count = 0;
const current = new Date(startDate);
while (current <= endDate) {
const day = current.getDay();
if (day !== 0 && day !== 6) count++;
current.setDate(current.getDate() + 1);
}
return count;
}
Este código cuenta días de lunes a viernes. Funciona bien para un test unitario. Falla en producción porque no considera feriados.
El segundo intento: hardcodear feriados
const FERIADOS_2026 = new Set([
"2026-01-01",
"2026-04-03",
"2026-04-04",
"2026-05-01",
// ...
]);
function countBusinessDays(startDate, endDate) {
let count = 0;
const current = new Date(startDate);
while (current <= endDate) {
const day = current.getDay();
const iso = current.toISOString().split("T")[0];
if (day !== 0 && day !== 6 && !FERIADOS_2026.has(iso)) count++;
current.setDate(current.getDate() + 1);
}
return count;
}
Funciona para 2026. El 1 de enero de 2027, el array está desactualizado y alguien tiene que acordarse de actualizarlo.
El tercer problema: zonas horarias
const fecha = new Date("2026-04-03");
console.log(fecha.toISOString()); // "2026-04-02T21:00:00.000Z" en UTC-3
Si tu servidor corre en UTC y tu usuario está en Santiago (UTC-3), new Date("2026-04-03") puede ser el 2 de abril en el servidor. El Viernes Santo se convierte en Jueves normal y el cálculo está mal.
El fix correcto:
function parseFechaLocal(dateString) {
const [year, month, day] = dateString.split("-").map(Number);
return new Date(year, month - 1, day); // mes es 0-indexed
}
O simplemente trabajar siempre con strings YYYY-MM-DD y no con objetos Date.
El cuarto problema: múltiples países
Si tu aplicación opera en Chile y Colombia, tienes que mantener dos listas de feriados. Y Colombia traslada sus feriados al lunes siguiente según la Ley Emiliani — eso significa que no puedes usar las fechas fijas, tienes que calcular el lunes correcto para cada año.
// Colombia: Epifanía es el 6 de enero, pero se celebra el lunes siguiente
function siguienteLunes(date) {
const d = new Date(date);
const dia = d.getDay();
if (dia === 1) return d; // ya es lunes
const diff = dia === 0 ? 1 : 8 - dia; // días hasta el próximo lunes
d.setDate(d.getDate() + diff);
return d;
}
const epifania2026 = siguienteLunes(new Date(2026, 0, 6));
// lunes 12 de enero
Esto hay que hacerlo para 8 feriados colombianos. Más los feriados móviles que dependen de Pascua.
Cuándo usar una API externa
La lógica DIY vale cuando:
- Solo necesitas un país
- El tráfico es bajo (puedes cachear el resultado)
- Tienes un proceso definido para actualizar los feriados cada año
- El riesgo de un error es bajo (app interna, no afecta cobros ni plazos legales)
La API externa vale cuando:
- Operas en 2+ países
- El riesgo de error tiene consecuencias (pagos, contratos, nóminas)
- No quieres mantener la lista manualmente
- Necesitas saber cuando un feriado extraordinario aparece
Implementación con la API de feriados.io
const API = "https://api.feriados.io/v1";
const HEADERS = { "Authorization": `Bearer ${process.env.FERIADOS_API_KEY}` };
// Contar días hábiles entre dos fechas (Team/Business)
async function countBusinessDays(pais, from, to) {
const res = await fetch(
`${API}/${pais}/business-days/between?from=${from}&to=${to}`,
{ headers: HEADERS }
);
const { data } = await res.json();
return data.business_days;
}
// Sumar N días hábiles a una fecha (Team/Business)
async function addBusinessDays(pais, date, days) {
const res = await fetch(
`${API}/${pais}/business-days/add?date=${date}&days=${days}`,
{ headers: HEADERS }
);
const { data } = await res.json();
return data.result_date;
}
// ¿Es hábil esta fecha? (Free)
async function isBusinessDay(pais, date) {
const res = await fetch(
`${API}/${pais}/is-business-day?date=${date}`,
{ headers: HEADERS }
);
const { data } = await res.json();
return data.is_business_day;
}
// Uso
const diasHabiles = await countBusinessDays("CL", "2026-04-01", "2026-04-30");
// 19 (abril tiene Semana Santa)
const fechaEntrega = await addBusinessDays("CO", "2026-04-01", 10);
// "2026-04-16" (Colombia tiene Jueves y Viernes Santo + festivo del 6 trasladado)
El resultado es el mismo para ambos países con el mismo código. Sin listas, sin lógica de traslados, sin bugs de zona horaria.
¿Necesitas esto en producción? Empezar gratis — API key en 30 segundos, sin tarjeta →
Ver también: Feriados Chile 2026: lista completa → · Feriados Colombia 2026 y la Ley Emiliani → · Documentación de endpoints →