Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/utils/__tests__/intlCache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { describe, it, expect, beforeEach } from 'vitest';
import {
getNumberFormat,
getDateTimeFormat,
getRelativeTimeFormat,
clearIntlCache,
getNumberFormatCacheSize,
getDateTimeFormatCacheSize,
getRelativeTimeFormatCacheSize,
} from '../intlCache';

describe('intlCache', () => {
Expand Down Expand Up @@ -58,6 +60,26 @@ describe('intlCache', () => {
});
});

describe('getRelativeTimeFormat', () => {
it('returns a valid Intl.RelativeTimeFormat instance', () => {
const formatter = getRelativeTimeFormat('en-US', { numeric: 'auto' });
expect(formatter).toBeInstanceOf(Intl.RelativeTimeFormat);
expect(formatter.format(-1, 'day')).toBe('yesterday');
});

it('caches formatters by locale and options', () => {
const formatter1 = getRelativeTimeFormat('en-US', { numeric: 'auto' });
const formatter2 = getRelativeTimeFormat('en-US', { numeric: 'auto' });
expect(formatter1).toBe(formatter2);
});

it('creates separate formatters for different locales', () => {
const formatterEN = getRelativeTimeFormat('en-US', { numeric: 'auto' });
const formatterFR = getRelativeTimeFormat('fr-FR', { numeric: 'auto' });
expect(formatterEN).not.toBe(formatterFR);
});
});

describe('cache sizes', () => {
it('returns correct number format cache size', () => {
expect(getNumberFormatCacheSize()).toBe(0);
Expand All @@ -72,14 +94,23 @@ describe('intlCache', () => {
expect(getDateTimeFormatCacheSize()).toBe(1);
});

it('clearIntlCache clears both caches', () => {
it('returns correct relative time format cache size', () => {
expect(getRelativeTimeFormatCacheSize()).toBe(0);
getRelativeTimeFormat('en-US');
expect(getRelativeTimeFormatCacheSize()).toBe(1);
});

it('clearIntlCache clears all caches', () => {
getNumberFormat('en-US');
getDateTimeFormat('en-US');
getRelativeTimeFormat('en-US');
expect(getNumberFormatCacheSize()).toBe(1);
expect(getDateTimeFormatCacheSize()).toBe(1);
expect(getRelativeTimeFormatCacheSize()).toBe(1);
clearIntlCache();
expect(getNumberFormatCacheSize()).toBe(0);
expect(getDateTimeFormatCacheSize()).toBe(0);
expect(getRelativeTimeFormatCacheSize()).toBe(0);
});
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/utils/dateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Pass an explicit locale to override; omit it to use the browser/system locale.
*/

import { getDateTimeFormat } from './intlCache';
import { getDateTimeFormat, getRelativeTimeFormat } from './intlCache';

export function formatDate(
date: Date | string | number,
Expand All @@ -23,7 +23,7 @@ export function formatTime(date: Date | string | number, locale?: string): strin

export function formatRelative(date: Date | string | number, locale?: string): string {
const diff = Math.round((new Date(date).getTime() - Date.now()) / 1000);
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
const rtf = getRelativeTimeFormat(locale, { numeric: 'auto' });

if (Math.abs(diff) < 60) return rtf.format(diff, 'second');
if (Math.abs(diff) < 3600) return rtf.format(Math.round(diff / 60), 'minute');
Expand Down
22 changes: 21 additions & 1 deletion src/utils/intlCache.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Intl Formatter Cache - Caches Intl.NumberFormat and Intl.DateTimeFormat instances
* Intl Formatter Cache - Caches Intl.NumberFormat, Intl.DateTimeFormat, and Intl.RelativeTimeFormat instances
*/

function serializeCacheKey(
Expand All @@ -11,6 +11,7 @@ function serializeCacheKey(

const numberFormatterCache = new Map<string, Intl.NumberFormat>();
const dateTimeFormatterCache = new Map<string, Intl.DateTimeFormat>();
const relativeTimeFormatterCache = new Map<string, Intl.RelativeTimeFormat>();

export function getNumberFormat(
locale: string | undefined,
Expand Down Expand Up @@ -40,9 +41,24 @@ export function getDateTimeFormat(
return formatter;
}

export function getRelativeTimeFormat(
locale: string | undefined,
options?: Intl.RelativeTimeFormatOptions,
): Intl.RelativeTimeFormat {
const resolvedLocale = locale ?? 'en-US';
const key = serializeCacheKey(resolvedLocale, options ?? {});
let formatter = relativeTimeFormatterCache.get(key);
if (!formatter) {
formatter = new Intl.RelativeTimeFormat(resolvedLocale, options);
relativeTimeFormatterCache.set(key, formatter);
}
return formatter;
}

export function clearIntlCache(): void {
numberFormatterCache.clear();
dateTimeFormatterCache.clear();
relativeTimeFormatterCache.clear();
}

export function getNumberFormatCacheSize(): number {
Expand All @@ -52,3 +68,7 @@ export function getNumberFormatCacheSize(): number {
export function getDateTimeFormatCacheSize(): number {
return dateTimeFormatterCache.size;
}

export function getRelativeTimeFormatCacheSize(): number {
return relativeTimeFormatterCache.size;
}
Loading