import { getCalendarHolidayStrings, toIsoDate } from "./holiday-availability.js"; type VacationSpan = { startDate: Date; endDate: Date; isHalfDay: boolean; }; type HolidayContext = { countryCode?: string | null | undefined; federalState?: string | null | undefined; metroCityName?: string | null | undefined; calendarHolidayStrings?: string[] | undefined; publicHolidayStrings?: string[] | undefined; }; type CountVacationChargeableDaysInput = HolidayContext & { vacation: VacationSpan; periodStart?: Date | undefined; periodEnd?: Date | undefined; }; function clampToDay(value: Date): Date { const date = new Date(value); date.setUTCHours(0, 0, 0, 0); return date; } function getOverlapRange( startDate: Date, endDate: Date, periodStart?: Date, periodEnd?: Date, ): { start: Date; end: Date } | null { const startBoundary = clampToDay(periodStart ?? startDate); const endBoundary = clampToDay(periodEnd ?? endDate); const overlapStart = clampToDay(new Date(Math.max(startDate.getTime(), startBoundary.getTime()))); const overlapEnd = clampToDay(new Date(Math.min(endDate.getTime(), endBoundary.getTime()))); if (overlapStart > overlapEnd) { return null; } return { start: overlapStart, end: overlapEnd }; } export function countCalendarDaysInPeriod( vacation: VacationSpan, periodStart?: Date, periodEnd?: Date, ): number { const overlap = getOverlapRange(vacation.startDate, vacation.endDate, periodStart, periodEnd); if (!overlap) { return 0; } if (vacation.isHalfDay) { return 0.5; } const ms = overlap.end.getTime() - overlap.start.getTime(); return Math.round(ms / 86_400_000) + 1; } export function countVacationChargeableDays( input: CountVacationChargeableDaysInput, ): number { const overlap = getOverlapRange( input.vacation.startDate, input.vacation.endDate, input.periodStart, input.periodEnd, ); if (!overlap) { return 0; } const holidaySet = new Set( input.calendarHolidayStrings ? input.calendarHolidayStrings.filter((isoDate) => isoDate >= toIsoDate(overlap.start) && isoDate <= toIsoDate(overlap.end)) : getCalendarHolidayStrings( overlap.start, overlap.end, input.countryCode, input.federalState, input.metroCityName, ), ); for (const isoDate of input.publicHolidayStrings ?? []) { if (isoDate >= toIsoDate(overlap.start) && isoDate <= toIsoDate(overlap.end)) { holidaySet.add(isoDate); } } if (input.vacation.isHalfDay) { return holidaySet.has(toIsoDate(overlap.start)) ? 0 : 0.5; } let total = 0; const cursor = new Date(overlap.start); while (cursor <= overlap.end) { if (!holidaySet.has(toIsoDate(cursor))) { total += 1; } cursor.setUTCDate(cursor.getUTCDate() + 1); } return total; }