You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
295 lines
7.2 KiB
295 lines
7.2 KiB
/**
|
|
* 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()
|
|
}
|
|
|