/** * Date Formatting Utilities * * Provides utilities for formatting dates in German locale and converting * between different date formats for display and database storage. */ /** * Format a date to German format (DD.MM.YYYY) * * @param date - Date to format (Date object, ISO string, or timestamp) * @returns Formatted date string (e.g., "24.12.2024") * * @example * formatDateGerman(new Date('2024-12-24')) * // => "24.12.2024" * * formatDateGerman('2024-12-24T10:30:00Z') * // => "24.12.2024" */ export function formatDateGerman(date: Date | string | number): string { const dateObj = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date if (isNaN(dateObj.getTime())) { return '' } return dateObj.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', }) } /** * Format a date to German long format (e.g., "24. Dezember 2024") * * @param date - Date to format * @returns Formatted date string with month name * * @example * formatDateGermanLong(new Date('2024-12-24')) * // => "24. Dezember 2024" */ export function formatDateGermanLong(date: Date | string | number): string { const dateObj = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date if (isNaN(dateObj.getTime())) { return '' } return dateObj.toLocaleDateString('de-DE', { day: 'numeric', month: 'long', year: 'numeric', }) } /** * Format a date with time to German format (DD.MM.YYYY HH:MM) * * @param date - Date to format * @returns Formatted datetime string * * @example * formatDateTimeGerman(new Date('2024-12-24T15:30:00Z')) * // => "24.12.2024 15:30" */ export function formatDateTimeGerman(date: Date | string | number): string { const dateObj = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date if (isNaN(dateObj.getTime())) { return '' } return dateObj.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }) } /** * Parse a date input string (DD.MM.YYYY or YYYY-MM-DD) to ISO format (YYYY-MM-DD) * Suitable for database storage and HTML date inputs * * @param dateString - Date string in DD.MM.YYYY or YYYY-MM-DD format * @returns ISO date string (YYYY-MM-DD) or null if invalid * * @example * parseDateToISO('24.12.2024') * // => "2024-12-24" * * parseDateToISO('2024-12-24') * // => "2024-12-24" * * parseDateToISO('invalid') * // => null */ export function parseDateToISO(dateString: string): string | null { if (!dateString || dateString.trim() === '') { return null } // Check if already in ISO format (YYYY-MM-DD) if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) { const date = new Date(dateString) return isNaN(date.getTime()) ? null : dateString } // Parse German format (DD.MM.YYYY) const germanMatch = dateString.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/) if (germanMatch) { const [, day, month, year] = germanMatch const paddedDay = day.padStart(2, '0') const paddedMonth = month.padStart(2, '0') const isoDate = `${year}-${paddedMonth}-${paddedDay}` // Validate the date const date = new Date(isoDate) if (isNaN(date.getTime())) { return null } return isoDate } return null } /** * Convert ISO date string (YYYY-MM-DD) to German format (DD.MM.YYYY) * * @param isoDate - ISO date string (YYYY-MM-DD) * @returns German date string (DD.MM.YYYY) or empty string if invalid * * @example * isoToGermanDate('2024-12-24') * // => "24.12.2024" */ export function isoToGermanDate(isoDate: string): string { if (!isoDate || !/^\d{4}-\d{2}-\d{2}$/.test(isoDate)) { return '' } const [year, month, day] = isoDate.split('-') return `${day}.${month}.${year}` } /** * Validate if a date of birth indicates a person is at least 18 years old * * @param dateOfBirth - Date of birth (Date object, ISO string, or German format) * @returns true if person is 18 or older * * @example * isAgeAtLeast18(new Date('2000-01-01')) * // => true (as of 2024) * * isAgeAtLeast18('01.01.2010') * // => false (as of 2024) */ export function isAgeAtLeast18(dateOfBirth: Date | string): boolean { let dateObj: Date if (typeof dateOfBirth === 'string') { // Try parsing German format first const isoDate = parseDateToISO(dateOfBirth) if (isoDate) { dateObj = new Date(isoDate) } else { dateObj = new Date(dateOfBirth) } } else { dateObj = dateOfBirth } if (isNaN(dateObj.getTime())) { return false } const today = new Date() const age = today.getFullYear() - dateObj.getFullYear() const monthDiff = today.getMonth() - dateObj.getMonth() const dayDiff = today.getDate() - dateObj.getDate() // Check if birthday has occurred this year if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) { return age - 1 >= 18 } return age >= 18 } /** * Format a relative date (e.g., "vor 2 Tagen", "gerade eben") * * @param date - Date to format * @returns Relative time string in German * * @example * formatRelativeDate(new Date(Date.now() - 1000 * 60 * 5)) * // => "vor 5 Minuten" */ export function formatRelativeDate(date: Date | string | number): string { const dateObj = typeof date === 'string' || typeof date === 'number' ? new Date(date) : date if (isNaN(dateObj.getTime())) { return '' } const now = new Date() const diffMs = now.getTime() - dateObj.getTime() const diffSecs = Math.floor(diffMs / 1000) const diffMins = Math.floor(diffSecs / 60) const diffHours = Math.floor(diffMins / 60) const diffDays = Math.floor(diffHours / 24) if (diffSecs < 60) { return 'gerade eben' } if (diffMins < 60) { return `vor ${diffMins} ${diffMins === 1 ? 'Minute' : 'Minuten'}` } if (diffHours < 24) { return `vor ${diffHours} ${diffHours === 1 ? 'Stunde' : 'Stunden'}` } if (diffDays < 7) { return `vor ${diffDays} ${diffDays === 1 ? 'Tag' : 'Tagen'}` } if (diffDays < 30) { const weeks = Math.floor(diffDays / 7) return `vor ${weeks} ${weeks === 1 ? 'Woche' : 'Wochen'}` } if (diffDays < 365) { const months = Math.floor(diffDays / 30) return `vor ${months} ${months === 1 ? 'Monat' : 'Monaten'}` } const years = Math.floor(diffDays / 365) return `vor ${years} ${years === 1 ? 'Jahr' : 'Jahren'}` } /** * Get today's date in ISO format (YYYY-MM-DD) * * @returns Today's date in ISO format * * @example * getTodayISO() * // => "2024-12-24" */ export function getTodayISO(): string { const today = new Date() return today.toISOString().split('T')[0] } /** * Validate if a date is in the future * * @param date - Date to validate * @returns true if date is in the future */ export function isDateInFuture(date: Date | string): boolean { const dateObj = typeof date === 'string' ? new Date(date) : date if (isNaN(dateObj.getTime())) { return false } return dateObj.getTime() > Date.now() } /** * Validate if a date is in the past * * @param date - Date to validate * @returns true if date is in the past */ export function isDateInPast(date: Date | string): boolean { const dateObj = typeof date === 'string' ? new Date(date) : date if (isNaN(dateObj.getTime())) { return false } return dateObj.getTime() < Date.now() }