/** * DateCalculator - Centralized date calculation logic for calendar * Handles all date computations with proper week start handling */ import { CalendarConfig } from '../core/CalendarConfig'; export class DateCalculator { private config: CalendarConfig; constructor(config: CalendarConfig) { this.config = config; } /** * Get dates for work week based on ISO week (starts Monday) * @param weekStart - Any date in the week * @returns Array of dates for the configured work days */ getWorkWeekDates(weekStart: Date): Date[] { const dates: Date[] = []; const workWeekSettings = this.config.getWorkWeekSettings(); // Get Monday of the ISO week const monday = this.getISOWeekStart(weekStart); // Calculate dates for each work day workWeekSettings.workDays.forEach(dayOfWeek => { const date = new Date(monday); if (dayOfWeek === 0) { // Sunday is 6 days after Monday date.setDate(monday.getDate() + 6); } else { // Monday=1 becomes 0 days after, Tuesday=2 becomes 1 day after, etc. date.setDate(monday.getDate() + dayOfWeek - 1); } dates.push(date); }); return dates; } /** * Get the start of the ISO week (Monday) for a given date * @param date - Any date in the week * @returns The Monday of the ISO week */ getISOWeekStart(date: Date): Date { const monday = new Date(date); const currentDay = monday.getDay(); const daysToSubtract = currentDay === 0 ? 6 : currentDay - 1; monday.setDate(monday.getDate() - daysToSubtract); monday.setHours(0, 0, 0, 0); return monday; } /** * Get the start of the week for a given date (legacy method) * @param date - Any date in the week * @param firstDayOfWeek - 0 for Sunday, 1 for Monday * @returns The start date of the week */ getWeekStart(date: Date, firstDayOfWeek: number = 1): Date { return this.getISOWeekStart(date); } /** * Get the end of the week for a given date * @param date - Any date in the week * @param firstDayOfWeek - 0 for Sunday, 1 for Monday * @returns The end date of the week */ getWeekEnd(date: Date, firstDayOfWeek: number = 1): Date { const weekStart = this.getWeekStart(date, firstDayOfWeek); const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate() + 6); weekEnd.setHours(23, 59, 59, 999); return weekEnd; } /** * Get week number for a date (ISO 8601) * @param date - The date to get week number for * @returns Week number (1-53) */ getWeekNumber(date: Date): number { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); const yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1)); return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1)/7); } /** * Format a date range for display * @param start - Start date * @param end - End date * @returns Formatted date range string */ formatDateRange(start: Date, end: Date): string { const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const startMonth = months[start.getMonth()]; const endMonth = months[end.getMonth()]; const startDay = start.getDate(); const endDay = end.getDate(); const startYear = start.getFullYear(); const endYear = end.getFullYear(); if (startYear !== endYear) { return `${startMonth} ${startDay}, ${startYear} - ${endMonth} ${endDay}, ${endYear}`; } else if (startMonth === endMonth) { return `${startMonth} ${startDay} - ${endDay}, ${startYear}`; } else { return `${startMonth} ${startDay} - ${endMonth} ${endDay}, ${startYear}`; } } /** * Format a date to ISO date string (YYYY-MM-DD) * @param date - Date to format * @returns ISO date string */ formatISODate(date: Date): string { return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; } /** * Check if a date is today * @param date - Date to check * @returns True if the date is today */ isToday(date: Date): boolean { const today = new Date(); return date.toDateString() === today.toDateString(); } /** * Add days to a date * @param date - Base date * @param days - Number of days to add (can be negative) * @returns New date */ addDays(date: Date, days: number): Date { const result = new Date(date); result.setDate(result.getDate() + days); return result; } /** * Add weeks to a date * @param date - Base date * @param weeks - Number of weeks to add (can be negative) * @returns New date */ addWeeks(date: Date, weeks: number): Date { return this.addDays(date, weeks * 7); } /** * Get all dates in a week * @param weekStart - Start of the week * @returns Array of 7 dates for the full week */ getFullWeekDates(weekStart: Date): Date[] { const dates: Date[] = []; for (let i = 0; i < 7; i++) { dates.push(this.addDays(weekStart, i)); } return dates; } /** * Get the day name for a date * @param date - Date to get day name for * @param format - 'short' or 'long' * @returns Day name */ getDayName(date: Date, format: 'short' | 'long' = 'short'): string { const days = { short: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], long: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] }; return days[format][date.getDay()]; } } // Create singleton instance with config export function createDateCalculator(config: CalendarConfig): DateCalculator { return new DateCalculator(config); }