60 lines
1.5 KiB
TypeScript
60 lines
1.5 KiB
TypeScript
|
|
import { useCallback, useEffect, useState } from 'react';
|
||
|
|
|
||
|
|
function formatLocalTime(timezone: string, hour12: boolean): string | undefined {
|
||
|
|
try {
|
||
|
|
return new Intl.DateTimeFormat('en', {
|
||
|
|
timeZone: timezone,
|
||
|
|
hour: 'numeric',
|
||
|
|
minute: '2-digit',
|
||
|
|
hour12,
|
||
|
|
}).format(new Date());
|
||
|
|
} catch {
|
||
|
|
return undefined;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function getTimezoneAbbr(timezone: string): string | undefined {
|
||
|
|
try {
|
||
|
|
return new Intl.DateTimeFormat('en', {
|
||
|
|
timeZone: timezone,
|
||
|
|
timeZoneName: 'short',
|
||
|
|
})
|
||
|
|
.formatToParts(new Date())
|
||
|
|
.find((p) => p.type === 'timeZoneName')?.value;
|
||
|
|
} catch {
|
||
|
|
return undefined;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export type LocalTimeInfo = {
|
||
|
|
time: string;
|
||
|
|
abbr: string | undefined;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Returns the current time (and timezone abbreviation) in the given IANA
|
||
|
|
* timezone, updated every minute. Returns undefined when timezone is
|
||
|
|
* undefined or invalid.
|
||
|
|
*/
|
||
|
|
export function useLocalTime(
|
||
|
|
timezone: string | undefined,
|
||
|
|
hour12: boolean = true,
|
||
|
|
): LocalTimeInfo | undefined {
|
||
|
|
const compute = useCallback((): LocalTimeInfo | undefined => {
|
||
|
|
if (!timezone) return undefined;
|
||
|
|
const time = formatLocalTime(timezone, hour12);
|
||
|
|
if (!time) return undefined;
|
||
|
|
return { time, abbr: getTimezoneAbbr(timezone) };
|
||
|
|
}, [timezone, hour12]);
|
||
|
|
|
||
|
|
const [info, setInfo] = useState<LocalTimeInfo | undefined>(compute);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
setInfo(compute());
|
||
|
|
const id = window.setInterval(() => setInfo(compute()), 60_000);
|
||
|
|
return () => window.clearInterval(id);
|
||
|
|
}, [compute]);
|
||
|
|
|
||
|
|
return info;
|
||
|
|
}
|