signoz/frontend/src/hooks/useTimezoneFormatter/useTimezoneFormatter.ts
Shaheer Kochai b333aa3775
Feat: Timezone picker feature (#6474)
* feat: time picker hint and timezone picker UI with basic functionality + helper to get timezones

* feat: add support for esc keypress to close the timezone picker

* chore: add the selected timezone as url param and close timezone picker on select

* fix: overall improvement + add searchIndex to timezone

* feat: timezone preferences UI

* chore: improve timezone utils

* chore: change timezone item from div to button

* feat: display timezone in timepicker input

* chore: fix the typo

* fix: don't focus on time picker when timezone is clicked

* fix: fix the issue of timezone breaking for browser and utc timezones

* fix: display the timezone in timepicker hint 'You are at'

* feat: timezone basic functionality (#6492)

* chore: change div to fragment + change type to any as the ESLint complains otherwise

* chore: manage etc timezone filtering with an arg

* chore: update timezone wrapper class name

* fix: add timezone support to downloaded logs

* feat: add current timezone to dashboard list and configure metadata modal

* fix: add pencil icon next to timezone hint + change the copy to Current timezone

* fix: properly handle the escape button behavior for timezone picker

* chore: replace @vvo/tzdb with native Intl API for timezones

* feat: lightmode for timezone picker and timezone adaptation components

* fix: use normald tz in browser timezone

* fix: timezone picker lightmode fixes

* feat: display selected time range in 12 hour format

* chore: remove unnecessary optional chaining

* fix: fix the typo in css variable

* chore: add em dash and change icon for timezone hint in date/time picker

* chore: move pen line icon to the right of timezone offset

* fix: fix the failing tests

* feat: handle switching off the timezone adaptation
2024-12-16 11:27:20 +05:30

104 lines
2.8 KiB
TypeScript

import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { useCallback, useEffect, useMemo } from 'react';
// Initialize dayjs plugins
dayjs.extend(utc);
dayjs.extend(timezone);
// Types
export type TimestampInput = string | number | Date;
interface CacheEntry {
value: string;
timestamp: number;
}
//
// Constants
const CACHE_SIZE_LIMIT = 1000;
const CACHE_CLEANUP_PERCENTAGE = 0.5; // Remove 50% when limit is reached
function useTimezoneFormatter({
userTimezone,
}: {
userTimezone: Timezone;
}): {
formatTimezoneAdjustedTimestamp: (
input: TimestampInput,
format?: string,
) => string;
} {
// Initialize cache using useMemo to persist between renders
const cache = useMemo(() => new Map<string, CacheEntry>(), []);
// Clear cache when timezone changes
useEffect(() => {
cache.clear();
}, [cache, userTimezone]);
const clearCacheEntries = useCallback(() => {
if (cache.size <= CACHE_SIZE_LIMIT) return;
// Sort entries by timestamp (oldest first)
const sortedEntries = Array.from(cache.entries()).sort(
(a, b) => a[1].timestamp - b[1].timestamp,
);
// Calculate how many entries to remove (50% or overflow, whichever is larger)
const entriesToRemove = Math.max(
Math.floor(cache.size * CACHE_CLEANUP_PERCENTAGE),
cache.size - CACHE_SIZE_LIMIT,
);
// Remove oldest entries
sortedEntries.slice(0, entriesToRemove).forEach(([key]) => cache.delete(key));
}, [cache]);
/**
* Formats a timestamp with the user's timezone and caches the result
* @param {TimestampInput} input - The timestamp to format (string, number, or Date)
* @param {string} [format='YYYY-MM-DD HH:mm:ss'] - The desired output format
* @returns {string} The formatted timestamp string in the user's timezone
* @example
* // Input: UTC timestamp
* // User timezone: 'UTC - 4'
* // Returns: "2024-03-14 15:30:00"
* formatTimezoneAdjustedTimestamp('2024-03-14T19:30:00Z')
*/
const formatTimezoneAdjustedTimestamp = useCallback(
(input: TimestampInput, format = 'YYYY-MM-DD HH:mm:ss'): string => {
const timestamp = dayjs(input).valueOf();
const cacheKey = `${timestamp}_${userTimezone.value}`;
// Check cache first
const cachedValue = cache.get(cacheKey);
if (cachedValue) {
return cachedValue.value;
}
// Format timestamp
const formattedValue = dayjs(input).tz(userTimezone.value).format(format);
// Update cache
cache.set(cacheKey, {
value: formattedValue,
timestamp: Date.now(),
});
// Clear expired entries and enforce size limit
if (cache.size > CACHE_SIZE_LIMIT) {
clearCacheEntries();
}
return formattedValue;
},
[cache, clearCacheEntries, userTimezone.value],
);
return { formatTimezoneAdjustedTimestamp };
}
export default useTimezoneFormatter;