From b5e24f10d38c03eccf8e585c0394c5d789513e93 Mon Sep 17 00:00:00 2001 From: Jared Pereira Date: Mon, 27 Oct 2025 15:14:08 -0400 Subject: [PATCH] add useLocalizedDate hook --- package-lock.json | 18 +++++++++++++++++ package.json | 2 ++ src/hooks/useLocalizedDate.ts | 37 +++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 src/hooks/useLocalizedDate.ts diff --git a/package-lock.json b/package-lock.json index c5bef979..0d806d73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "ioredis": "^5.6.1", "katex": "^0.16.22", "linkifyjs": "^4.2.0", + "luxon": "^3.7.2", "multiformats": "^13.3.2", "next": "^15.5.3", "pg": "^8.16.3", @@ -88,6 +89,7 @@ "@cloudflare/workers-types": "^4.20240512.0", "@tailwindcss/postcss": "^4.1.13", "@types/katex": "^0.16.7", + "@types/luxon": "^3.7.1", "@types/node": "^22.15.17", "@types/react": "19.1.3", "@types/react-dom": "19.1.3", @@ -6614,6 +6616,13 @@ "license": "MIT", "peer": true }, + "node_modules/@types/luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", @@ -12264,6 +12273,15 @@ "es5-ext": "~0.10.2" } }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", diff --git a/package.json b/package.json index 47ae9f47..19bffcf9 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "ioredis": "^5.6.1", "katex": "^0.16.22", "linkifyjs": "^4.2.0", + "luxon": "^3.7.2", "multiformats": "^13.3.2", "next": "^15.5.3", "pg": "^8.16.3", @@ -98,6 +99,7 @@ "@cloudflare/workers-types": "^4.20240512.0", "@tailwindcss/postcss": "^4.1.13", "@types/katex": "^0.16.7", + "@types/luxon": "^3.7.1", "@types/node": "^22.15.17", "@types/react": "19.1.3", "@types/react-dom": "19.1.3", diff --git a/src/hooks/useLocalizedDate.ts b/src/hooks/useLocalizedDate.ts new file mode 100644 index 00000000..289aff3d --- /dev/null +++ b/src/hooks/useLocalizedDate.ts @@ -0,0 +1,37 @@ +"use client"; +import { useContext, useMemo } from "react"; +import { DateTime } from "luxon"; +import { RequestHeadersContext } from "components/Providers/RequestHeadersProvider"; + +/** + * Hook that formats a date string using Luxon with timezone and locale from request headers. + * + * @param dateString - ISO date string to format + * @param options - Intl.DateTimeFormatOptions for formatting + * @returns Formatted date string + * + * @example + * const formatted = useLocalizedDate("2024-01-15T10:30:00Z", { dateStyle: 'full', timeStyle: 'short' }); + */ +export function useLocalizedDate( + dateString: string, + options?: Intl.DateTimeFormatOptions, +): string { + const { timezone, language } = useContext(RequestHeadersContext); + + return useMemo(() => { + // Parse the date string to Luxon DateTime + let dateTime = DateTime.fromISO(dateString); + + // Apply timezone if available + if (timezone) { + dateTime = dateTime.setZone(timezone); + } + + // Parse locale from accept-language header (take first locale) + // accept-language format: "en-US,en;q=0.9,es;q=0.8" + const locale = language?.split(",")[0]?.split(";")[0]?.trim() || "en-US"; + + return dateTime.toLocaleString(options, { locale }); + }, [dateString, options, timezone, language]); +} -- 2.51.2