diff options
| author | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-24 13:09:50 +0000 |
| commit | 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b (patch) | |
| tree | b9df4ca6a70db45cfffbae6fdd7252e20fb8e93c /src/components/hooks/useTimezone.ts | |
| download | umami-396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b.tar.xz umami-396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b.zip | |
Created from https://vercel.com/new
Diffstat (limited to 'src/components/hooks/useTimezone.ts')
| -rw-r--r-- | src/components/hooks/useTimezone.ts | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/src/components/hooks/useTimezone.ts b/src/components/hooks/useTimezone.ts new file mode 100644 index 0000000..ef25539 --- /dev/null +++ b/src/components/hooks/useTimezone.ts @@ -0,0 +1,95 @@ +import { formatInTimeZone, utcToZonedTime, zonedTimeToUtc } from 'date-fns-tz'; +import { TIMEZONE_CONFIG, TIMEZONE_LEGACY } from '@/lib/constants'; +import { getTimezone } from '@/lib/date'; +import { setItem } from '@/lib/storage'; +import { setTimezone, useApp } from '@/store/app'; +import { useLocale } from './useLocale'; + +const selector = (state: { timezone: string }) => state.timezone; + +export function useTimezone() { + const timezone = useApp(selector); + const localTimeZone = getTimezone(); + const { dateLocale } = useLocale(); + + const saveTimezone = (value: string) => { + setItem(TIMEZONE_CONFIG, value); + setTimezone(value); + }; + + const formatTimezoneDate = (date: string, pattern: string) => { + return formatInTimeZone( + /^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$/.test(date) + ? date + : `${date.split(' ').join('T')}Z`, + timezone, + pattern, + { locale: dateLocale }, + ); + }; + + const formatSeriesTimezone = (data: any, column: string, timezone: string) => { + return data.map(item => { + const date = new Date(item[column]); + + const format = new Intl.DateTimeFormat('en-US', { + timeZone: timezone, + hour12: false, + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + }); + + const parts = format.formatToParts(date); + const get = type => parts.find(p => p.type === type)?.value; + + const year = get('year'); + const month = get('month'); + const day = get('day'); + const hour = get('hour'); + const minute = get('minute'); + const second = get('second'); + + return { + ...item, + [column]: `${year}-${month}-${day} ${hour}:${minute}:${second}`, + }; + }); + }; + + const toUtc = (date: Date | string | number) => { + return zonedTimeToUtc(date, timezone); + }; + + const fromUtc = (date: Date | string | number) => { + return utcToZonedTime(date, timezone); + }; + + const localToUtc = (date: Date | string | number) => { + return zonedTimeToUtc(date, localTimeZone); + }; + + const localFromUtc = (date: Date | string | number) => { + return utcToZonedTime(date, localTimeZone); + }; + + const canonicalizeTimezone = (timezone: string): string => { + return TIMEZONE_LEGACY[timezone] ?? timezone; + }; + + return { + timezone, + localTimeZone, + toUtc, + fromUtc, + localToUtc, + localFromUtc, + saveTimezone, + formatTimezoneDate, + formatSeriesTimezone, + canonicalizeTimezone, + }; +} |