Calendar/src/v2/core/DateService.ts
Janus C. H. Knudsen 0eb3bacb41 Introduces uniform column key concept for calendar events
Refactors column identification with a new buildColumnKey method to support flexible date and resource tracking

Replaces separate dateKey and resourceId handling with a unified columnKey approach
Improves column rendering and event management with more consistent key generation
Simplifies cross-component event tracking and column lookups
2025-12-13 11:46:57 +01:00

145 lines
4.3 KiB
TypeScript

import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import isoWeek from 'dayjs/plugin/isoWeek';
import { ITimeFormatConfig } from './ITimeFormatConfig';
// Enable dayjs plugins
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(isoWeek);
export class DateService {
private timezone: string;
constructor(private config: ITimeFormatConfig) {
this.timezone = config.timezone;
}
parseISO(isoString: string): Date {
return dayjs(isoString).toDate();
}
getDayName(date: Date, format: 'short' | 'long' = 'short'): string {
return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date);
}
getWeekDates(offset = 0, days = 7): string[] {
const monday = dayjs().startOf('week').add(1, 'day').add(offset, 'week');
return Array.from({ length: days }, (_, i) =>
monday.add(i, 'day').format('YYYY-MM-DD')
);
}
// ============================================
// FORMATTING
// ============================================
formatTime(date: Date, showSeconds = false): string {
const pattern = showSeconds ? 'HH:mm:ss' : 'HH:mm';
return dayjs(date).format(pattern);
}
formatTimeRange(start: Date, end: Date): string {
return `${this.formatTime(start)} - ${this.formatTime(end)}`;
}
formatDate(date: Date): string {
return dayjs(date).format('YYYY-MM-DD');
}
getDateKey(date: Date): string {
return this.formatDate(date);
}
// ============================================
// COLUMN KEY
// ============================================
/**
* Build a uniform columnKey from grouping segments
* Handles any combination of date, resource, team, etc.
*
* @example
* buildColumnKey({ date: '2025-12-09' }) → "2025-12-09"
* buildColumnKey({ date: '2025-12-09', resource: 'EMP001' }) → "2025-12-09:EMP001"
*/
buildColumnKey(segments: Record<string, string>): string {
// Always put date first if present, then other segments alphabetically
const date = segments.date;
const others = Object.entries(segments)
.filter(([k]) => k !== 'date')
.sort(([a], [b]) => a.localeCompare(b))
.map(([, v]) => v);
return date ? [date, ...others].join(':') : others.join(':');
}
/**
* Parse a columnKey back into segments
* Assumes format: "date:resource:..." or just "date"
*/
parseColumnKey(columnKey: string): { date: string; resource?: string } {
const parts = columnKey.split(':');
return {
date: parts[0],
resource: parts[1]
};
}
/**
* Extract dateKey from columnKey (first segment)
*/
getDateFromColumnKey(columnKey: string): string {
return columnKey.split(':')[0];
}
// ============================================
// TIME CALCULATIONS
// ============================================
timeToMinutes(timeString: string): number {
const parts = timeString.split(':').map(Number);
const hours = parts[0] || 0;
const minutes = parts[1] || 0;
return hours * 60 + minutes;
}
minutesToTime(totalMinutes: number): string {
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return dayjs().hour(hours).minute(minutes).format('HH:mm');
}
getMinutesSinceMidnight(date: Date): number {
const d = dayjs(date);
return d.hour() * 60 + d.minute();
}
// ============================================
// UTC CONVERSIONS
// ============================================
toUTC(localDate: Date): string {
return dayjs.tz(localDate, this.timezone).utc().toISOString();
}
fromUTC(utcString: string): Date {
return dayjs.utc(utcString).tz(this.timezone).toDate();
}
// ============================================
// DATE CREATION
// ============================================
createDateAtTime(baseDate: Date | string, timeString: string): Date {
const totalMinutes = this.timeToMinutes(timeString);
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return dayjs(baseDate).startOf('day').hour(hours).minute(minutes).toDate();
}
getISOWeekDay(date: Date | string): number {
return dayjs(date).isoWeekday(); // 1=Monday, 7=Sunday
}
}