@eristack/business-calendar
Version:
Business days and holidays on YYYY-MM-DD wall dates — no timestamp import in core
1 lines • 6.88 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/core/errors.ts","../src/core/wall-date.ts","../src/core/calendar.ts"],"sourcesContent":["export {\n BusinessCalendarParseError,\n addWallDays,\n createBusinessCalendar,\n normalizeWallDate,\n wallDateDayOfWeek,\n} from \"./core/index.js\";\nexport type {\n BusinessCalendar,\n BusinessCalendarOptions,\n WallDate,\n} from \"./core/index.js\";\n","export class BusinessCalendarParseError extends Error {\n readonly code = \"BUSINESS_CALENDAR_PARSE_ERROR\" as const;\n\n constructor(message: string) {\n super(message);\n this.name = \"BusinessCalendarParseError\";\n }\n}\n","import { BusinessCalendarParseError } from \"./errors.js\";\n\n/** Calendar date `YYYY-MM-DD` (no time zone — wall intent). */\nexport type WallDate = string;\n\nconst WALL_DATE_RE = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\n\nexport function normalizeWallDate(value: string): WallDate {\n const trimmed = value.trim();\n const match = WALL_DATE_RE.exec(trimmed);\n if (!match) {\n throw new BusinessCalendarParseError(\n `Invalid wall date \"${value}\" — expected YYYY-MM-DD`,\n );\n }\n const year = Number(match[1]);\n const month = Number(match[2]);\n const day = Number(match[3]);\n const utc = Date.UTC(year, month - 1, day);\n const check = new Date(utc);\n if (\n check.getUTCFullYear() !== year ||\n check.getUTCMonth() !== month - 1 ||\n check.getUTCDate() !== day\n ) {\n throw new BusinessCalendarParseError(`Invalid calendar date \"${value}\"`);\n }\n const mm = String(month).padStart(2, \"0\");\n const dd = String(day).padStart(2, \"0\");\n return `${year}-${mm}-${dd}`;\n}\n\nexport function wallDateDayOfWeek(date: WallDate): number {\n const normalized = normalizeWallDate(date);\n const [y, m, d] = normalized.split(\"-\").map(Number) as [number, number, number];\n return new Date(Date.UTC(y, m - 1, d)).getUTCDay();\n}\n\nexport function addWallDays(date: WallDate, days: number): WallDate {\n const normalized = normalizeWallDate(date);\n const [y, m, d] = normalized.split(\"-\").map(Number) as [number, number, number];\n const next = new Date(Date.UTC(y, m - 1, d));\n next.setUTCDate(next.getUTCDate() + days);\n const ny = next.getUTCFullYear();\n const nm = String(next.getUTCMonth() + 1).padStart(2, \"0\");\n const nd = String(next.getUTCDate()).padStart(2, \"0\");\n return `${ny}-${nm}-${nd}`;\n}\n","import { BusinessCalendarParseError } from \"./errors.js\";\nimport type { BusinessCalendar, BusinessCalendarOptions } from \"./types.js\";\nimport {\n addWallDays,\n normalizeWallDate,\n wallDateDayOfWeek,\n type WallDate,\n} from \"./wall-date.js\";\n\nfunction normalizeWeekendDays(days: readonly number[]): ReadonlySet<number> {\n const set = new Set<number>();\n for (const day of days) {\n if (!Number.isInteger(day) || day < 0 || day > 6) {\n throw new BusinessCalendarParseError(\n `weekendDays entry ${day} must be an integer 0–6 (Sun–Sat)`,\n );\n }\n set.add(day);\n }\n return set;\n}\n\nexport function createBusinessCalendar(\n options: BusinessCalendarOptions,\n): BusinessCalendar {\n const weekendDays = normalizeWeekendDays(options.weekendDays);\n const holidays = new Set(\n options.holidays.map((h) => normalizeWallDate(h)),\n );\n\n function isBusinessDay(date: string): boolean {\n const wall = normalizeWallDate(date);\n if (holidays.has(wall)) {\n return false;\n }\n const dow = wallDateDayOfWeek(wall);\n return !weekendDays.has(dow);\n }\n\n function nextBusinessDay(date: string): WallDate {\n let cursor = normalizeWallDate(date);\n do {\n cursor = addWallDays(cursor, 1);\n } while (!isBusinessDay(cursor));\n return cursor;\n }\n\n function addBusinessDays(date: string, days: number): WallDate {\n if (!Number.isInteger(days)) {\n throw new BusinessCalendarParseError(\"days must be an integer\");\n }\n let cursor = normalizeWallDate(date);\n if (days === 0) {\n return cursor;\n }\n const step = days > 0 ? 1 : -1;\n let remaining = Math.abs(days);\n while (remaining > 0) {\n cursor = addWallDays(cursor, step);\n if (isBusinessDay(cursor)) {\n remaining -= 1;\n }\n }\n return cursor;\n }\n\n return { isBusinessDay, nextBusinessDay, addBusinessDays };\n}\n"],"mappings":"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","names":[]}