# TimeFormatter Specification ## Problem - Alle events i systemet/mock JSON er i Zulu tid (UTC) - Nuværende formatTime() metoder håndterer ikke timezone konvertering - Ingen support for 12/24 timers format baseret på configuration - Duplikeret formattering logik flere steder ## Løsning: Centraliseret TimeFormatter ### Requirements 1. **Timezone Support** - Konverter fra UTC/Zulu til brugerens lokale timezone - Respekter browser timezone settings - Håndter sommertid korrekt 2. **12/24 Timer Format** - Læs format præference fra CalendarConfig - Support både 12-timer (AM/PM) og 24-timer format - Gør det konfigurerbart per bruger 3. **Centralisering** - Én enkelt kilde til al tidsformattering - Konsistent formattering gennem hele applikationen - Nem at teste og vedligeholde ### Proposed Implementation ```typescript // src/utils/TimeFormatter.ts export class TimeFormatter { private static use24HourFormat: boolean = false; private static userTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone; /** * Initialize formatter with user preferences */ static initialize(config: { use24Hour: boolean; timezone?: string }) { this.use24HourFormat = config.use24Hour; if (config.timezone) { this.userTimezone = config.timezone; } } /** * Format UTC/Zulu time to local time with correct format * @param input - UTC Date, ISO string, or minutes from midnight * @returns Formatted time string in user's preferred format */ static formatTime(input: Date | string | number): string { let date: Date; if (typeof input === 'number') { // Minutes from midnight - create date for today const today = new Date(); today.setHours(0, 0, 0, 0); today.setMinutes(input); date = today; } else if (typeof input === 'string') { // ISO string - parse as UTC date = new Date(input); } else { date = input; } // Convert to local timezone const localDate = this.convertToLocalTime(date); // Format based on user preference if (this.use24HourFormat) { return this.format24Hour(localDate); } else { return this.format12Hour(localDate); } } /** * Convert UTC date to local timezone */ private static convertToLocalTime(utcDate: Date): Date { // Use Intl.DateTimeFormat for proper timezone conversion const formatter = new Intl.DateTimeFormat('en-US', { timeZone: this.userTimezone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); const parts = formatter.formatToParts(utcDate); const dateParts: any = {}; parts.forEach(part => { dateParts[part.type] = part.value; }); return new Date( parseInt(dateParts.year), parseInt(dateParts.month) - 1, parseInt(dateParts.day), parseInt(dateParts.hour), parseInt(dateParts.minute), parseInt(dateParts.second) ); } /** * Format time in 24-hour format (HH:mm) */ private static format24Hour(date: Date): string { const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } /** * Format time in 12-hour format (h:mm AM/PM) */ private static format12Hour(date: Date): string { const hours = date.getHours(); const minutes = date.getMinutes(); const period = hours >= 12 ? 'PM' : 'AM'; const displayHours = hours > 12 ? hours - 12 : (hours === 0 ? 12 : hours); return `${displayHours}:${minutes.toString().padStart(2, '0')} ${period}`; } /** * Format date and time together */ static formatDateTime(date: Date | string): string { const localDate = typeof date === 'string' ? new Date(date) : date; const convertedDate = this.convertToLocalTime(localDate); const dateStr = convertedDate.toLocaleDateString(); const timeStr = this.formatTime(convertedDate); return `${dateStr} ${timeStr}`; } /** * Get timezone offset in hours */ static getTimezoneOffset(): number { return new Date().getTimezoneOffset() / -60; } } ``` ### Configuration Integration ```typescript // src/core/CalendarConfig.ts export interface TimeFormatSettings { use24HourFormat: boolean; timezone?: string; // Optional override, defaults to browser timezone } // Add to CalendarConfig getTimeFormatSettings(): TimeFormatSettings { return { use24HourFormat: this.config.use24HourFormat ?? false, timezone: this.config.timezone // undefined = use browser default }; } ``` ### Usage Examples ```typescript // Initialize on app start TimeFormatter.initialize({ use24Hour: calendarConfig.getTimeFormatSettings().use24HourFormat, timezone: calendarConfig.getTimeFormatSettings().timezone }); // Format UTC event time to local const utcEventTime = "2024-01-15T14:30:00Z"; // 2:30 PM UTC const localTime = TimeFormatter.formatTime(utcEventTime); // Result (Copenhagen, 24h): "15:30" // Result (Copenhagen, 12h): "3:30 PM" // Result (New York, 12h): "9:30 AM" // Format minutes from midnight const minutes = 570; // 9:30 AM const formatted = TimeFormatter.formatTime(minutes); // Result (24h): "09:30" // Result (12h): "9:30 AM" ``` ### Testing Considerations 1. Test timezone conversions: - UTC to Copenhagen (UTC+1/+2) - UTC to New York (UTC-5/-4) - UTC to Tokyo (UTC+9) 2. Test daylight saving transitions 3. Test 12/24 hour format switching 4. Test edge cases: - Midnight (00:00 / 12:00 AM) - Noon (12:00 / 12:00 PM) - Events spanning multiple days ### Migration Plan 1. Implement TimeFormatter class 2. Add configuration options to CalendarConfig 3. Replace all existing formatTime() calls 4. Update mock data loader to handle UTC properly 5. Test thoroughly with different timezones