/** * Deterministic pseudo-random number in [0, 1) from id + salt. * Using a simple LCG hash so values are identical on server and client * (avoids React hydration mismatches from Math.random()). */ export function sp(id: number, salt: number): number { const n = ((id * 9301 + salt * 49297 + 1) % 233280 + 233280) % 233280 return n / 233280 } /** * Human-readable relative time string (German) for a UTC timestamp. * The `created_at` value from SQLite has no timezone suffix, so we append 'Z'. */ export function relativeTime(created_at: string): string { const now = Date.now() const created = new Date(created_at.endsWith('Z') ? created_at : created_at + 'Z').getTime() const diffMs = now - created const minutes = Math.floor(diffMs / 60000) const hours = Math.floor(diffMs / 3600000) const days = Math.floor(diffMs / 86400000) if (minutes < 1) return 'gerade eben' if (minutes < 60) return `vor ${minutes} Min.` if (hours < 24) return `vor ${hours} Std.` return `vor ${days} ${days === 1 ? 'Tag' : 'Tagen'}` } /** * Formats a German date string from year/month/day components. */ export function formatDate( year: string, month?: string | null, day?: string | null ): string { if (day && month) { const monthNames = ['Jan', 'Feb', 'März', 'Apr', 'Mai', 'Juni', 'Juli', 'Aug', 'Sept', 'Okt', 'Nov', 'Dez'] const monthName = monthNames[parseInt(month) - 1] || month return `${day}. ${monthName} ${year}` } else if (month) { const monthNames = [ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember', ] const monthName = monthNames[parseInt(month) - 1] || `Monat ${month}` return `${monthName} ${year}` } return year }