import { DateTime, IANAZone } from 'luxon'; const CONTINENT_ICONS: Record = { Africa: 'globe-africa', America: 'globe-americas', Antarctica: 'globe', Arctic: 'globe', Asia: 'globe-asia', Atlantic: 'globe-americas', Australia: 'globe-asia', Europe: 'globe-europe', Indian: 'globe-asia', Pacific: 'globe-asia', }; const timezoneOverrides: Record = { 'Europe/Kiev': 'Europe/Kyiv', }; export interface TimezoneInfo { timezone: string; area: string; location: string; displayLocation: string; icon: string; offset: number; offsetFormatted: string; short: string; long: string; } export default () => { const detectBrowserTimezone = (): string => { return DateTime.local().zone.name; }; const getTimezoneInfo = (timezone: string | null): TimezoneInfo | null => { if (!timezone) { return null; } const parts = timezone.split('/'); const displayParts = timezoneDisplayName(timezone).split('/'); const area = parts[0]; const location = parts[parts.length - 1].replace(/_/g, ' '); const displayLocation = displayParts[parts.length - 1].replace(/_/g, ' '); const tz = new IANAZone(timezone); if (!tz.isValid) { return null; } const dt = DateTime.local().setZone(tz); return { timezone, area, location, displayLocation, icon: CONTINENT_ICONS[area], offset: dt.offset, // @ts-expect-error: DateTime.ts: number not in typings but in code (probably version mismatch) offsetFormatted: tz.formatOffset(dt.ts, 'short'), short: dt.offsetNameShort, long: dt.offsetNameLong, }; }; const timezoneDisplayName = (timezone: string): string => { return (timezoneOverrides[timezone] || timezone).replaceAll('_', ' '); }; return { detectBrowserTimezone, getTimezoneInfo, timezoneDisplayName, }; };