Refactors date handling for ISO week compatibility
Centralizes all date calculations into a new `DateCalculator` class for better maintainability and consistency. Ensures correct ISO week handling (Monday as the first day) throughout the calendar. Updates `CalendarConfig` to use ISO day numbering (1-7 for Mon-Sun) for work week definitions. Fixes issue where date calculations were inconsistent. Enhances event rendering and navigation. Updates navigation logic to use pre-rendered events. Removes the need for `CONTAINER_READY_FOR_EVENTS` event.
This commit is contained in:
parent
efc1742dad
commit
7d513600d8
13 changed files with 230 additions and 343 deletions
|
|
@ -13,7 +13,7 @@ export class DateCalculator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get dates for work week based on ISO week (starts Monday)
|
||||
* Get dates for work week using ISO 8601 day numbering (Monday=1, Sunday=7)
|
||||
* @param weekStart - Any date in the week
|
||||
* @returns Array of dates for the configured work days
|
||||
*/
|
||||
|
|
@ -21,21 +21,15 @@ export class DateCalculator {
|
|||
const dates: Date[] = [];
|
||||
const workWeekSettings = this.config.getWorkWeekSettings();
|
||||
|
||||
// Get Monday of the ISO week
|
||||
const monday = this.getISOWeekStart(weekStart);
|
||||
// Always use ISO week start (Monday)
|
||||
const mondayOfWeek = 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);
|
||||
}
|
||||
|
||||
// Calculate dates for each work day using ISO numbering
|
||||
workWeekSettings.workDays.forEach(isoDay => {
|
||||
const date = new Date(mondayOfWeek);
|
||||
// ISO day 1=Monday is +0 days, ISO day 7=Sunday is +6 days
|
||||
const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1;
|
||||
date.setDate(mondayOfWeek.getDate() + daysFromMonday);
|
||||
dates.push(date);
|
||||
});
|
||||
|
||||
|
|
@ -56,24 +50,14 @@ export class DateCalculator {
|
|||
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
|
||||
* Get the end of the ISO 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
|
||||
* @returns The end date of the ISO week (Sunday)
|
||||
*/
|
||||
getWeekEnd(date: Date, firstDayOfWeek: number = 1): Date {
|
||||
const weekStart = this.getWeekStart(date, firstDayOfWeek);
|
||||
getWeekEnd(date: Date): Date {
|
||||
const weekStart = this.getISOWeekStart(date);
|
||||
const weekEnd = new Date(weekStart);
|
||||
weekEnd.setDate(weekStart.getDate() + 6);
|
||||
weekEnd.setHours(23, 59, 59, 999);
|
||||
|
|
@ -94,28 +78,40 @@ export class DateCalculator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Format a date range for display
|
||||
* Format a date range with customizable options
|
||||
* @param start - Start date
|
||||
* @param end - End date
|
||||
* @param options - Formatting options
|
||||
* @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'];
|
||||
formatDateRange(
|
||||
start: Date,
|
||||
end: Date,
|
||||
options: {
|
||||
locale?: string;
|
||||
month?: 'numeric' | '2-digit' | 'long' | 'short' | 'narrow';
|
||||
day?: 'numeric' | '2-digit';
|
||||
year?: 'numeric' | '2-digit';
|
||||
} = {}
|
||||
): string {
|
||||
const { locale = 'en-US', month = 'short', day = 'numeric' } = options;
|
||||
|
||||
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}`;
|
||||
const formatter = new Intl.DateTimeFormat(locale, {
|
||||
month,
|
||||
day,
|
||||
year: startYear !== endYear ? 'numeric' : undefined
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
if (typeof formatter.formatRange === 'function') {
|
||||
// @ts-ignore
|
||||
return formatter.formatRange(start, end);
|
||||
}
|
||||
|
||||
return `${formatter.format(start)} - ${formatter.format(end)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -173,18 +169,110 @@ export class DateCalculator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the day name for a date
|
||||
* Get the day name for a date using Intl.DateTimeFormat
|
||||
* @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()];
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
weekday: format
|
||||
});
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time to HH:MM
|
||||
* @param date - Date to format
|
||||
* @returns Time string
|
||||
*/
|
||||
formatTime(date: Date): string {
|
||||
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format time to 12-hour format
|
||||
* @param date - Date to format
|
||||
* @returns 12-hour time string
|
||||
*/
|
||||
formatTime12(date: Date): string {
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
const period = hours >= 12 ? 'PM' : 'AM';
|
||||
const displayHours = hours % 12 || 12;
|
||||
|
||||
return `${displayHours}:${String(minutes).padStart(2, '0')} ${period}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert minutes since midnight to time string
|
||||
* @param minutes - Minutes since midnight
|
||||
* @returns Time string
|
||||
*/
|
||||
minutesToTime(minutes: number): string {
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const mins = minutes % 60;
|
||||
const period = hours >= 12 ? 'PM' : 'AM';
|
||||
const displayHours = hours % 12 || 12;
|
||||
|
||||
return `${displayHours}:${String(mins).padStart(2, '0')} ${period}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert time string to minutes since midnight
|
||||
* @param timeStr - Time string
|
||||
* @returns Minutes since midnight
|
||||
*/
|
||||
timeToMinutes(timeStr: string): number {
|
||||
const [time] = timeStr.split('T').pop()!.split('.');
|
||||
const [hours, minutes] = time.split(':').map(Number);
|
||||
return hours * 60 + minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get minutes since start of day
|
||||
* @param date - Date or ISO string
|
||||
* @returns Minutes since midnight
|
||||
*/
|
||||
getMinutesSinceMidnight(date: Date | string): number {
|
||||
const d = typeof date === 'string' ? new Date(date) : date;
|
||||
return d.getHours() * 60 + d.getMinutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate duration in minutes between two dates
|
||||
* @param start - Start date or ISO string
|
||||
* @param end - End date or ISO string
|
||||
* @returns Duration in minutes
|
||||
*/
|
||||
getDurationMinutes(start: Date | string, end: Date | string): number {
|
||||
const startDate = typeof start === 'string' ? new Date(start) : start;
|
||||
const endDate = typeof end === 'string' ? new Date(end) : end;
|
||||
return Math.floor((endDate.getTime() - startDate.getTime()) / 60000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two dates are on the same day
|
||||
* @param date1 - First date
|
||||
* @param date2 - Second date
|
||||
* @returns True if same day
|
||||
*/
|
||||
isSameDay(date1: Date, date2: Date): boolean {
|
||||
return date1.toDateString() === date2.toDateString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if event spans multiple days
|
||||
* @param start - Start date or ISO string
|
||||
* @param end - End date or ISO string
|
||||
* @returns True if spans multiple days
|
||||
*/
|
||||
isMultiDay(start: Date | string, end: Date | string): boolean {
|
||||
const startDate = typeof start === 'string' ? new Date(start) : start;
|
||||
const endDate = typeof end === 'string' ? new Date(end) : end;
|
||||
return !this.isSameDay(startDate, endDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create singleton instance with config
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue