a tool for shared writing and social publishing
1"use client";
2import { useContext, useMemo } from "react";
3import { DateTime } from "luxon";
4import { RequestHeadersContext } from "components/Providers/RequestHeadersProvider";
5import { useHasPageLoaded } from "components/InitialPageLoadProvider";
6
7/**
8 * Hook that formats a date string using Luxon with timezone and locale from request headers.
9 * On initial page load, uses the timezone from request headers. After hydration, uses the system timezone.
10 *
11 * @param dateString - ISO date string to format
12 * @param options - Intl.DateTimeFormatOptions for formatting
13 * @returns Formatted date string
14 *
15 * @example
16 * const formatted = useLocalizedDate("2024-01-15T10:30:00Z", { dateStyle: 'full', timeStyle: 'short' });
17 */
18export function useLocalizedDate(
19 dateString: string,
20 options?: Intl.DateTimeFormatOptions,
21): string {
22 const { timezone, language } = useContext(RequestHeadersContext);
23 const hasPageLoaded = useHasPageLoaded();
24
25 return useMemo(() => {
26 // Parse the date string to Luxon DateTime
27 let dateTime = DateTime.fromISO(dateString);
28
29 // On initial page load, use header timezone. After hydration, use system timezone
30 const effectiveTimezone = !hasPageLoaded
31 ? timezone || "UTC"
32 : Intl.DateTimeFormat().resolvedOptions().timeZone;
33
34 // Apply timezone if available
35 if (effectiveTimezone) {
36 dateTime = dateTime.setZone(effectiveTimezone);
37 }
38
39 // On initial page load, use header locale. After hydration, use system locale
40 // Parse locale from accept-language header (take first locale)
41 // accept-language format: "en-US,en;q=0.9,es;q=0.8"
42 const effectiveLocale = !hasPageLoaded
43 ? language?.split(",")[0]?.split(";")[0]?.trim() || "en-US"
44 : Intl.DateTimeFormat().resolvedOptions().locale;
45
46 try {
47 return dateTime.toLocaleString(options, { locale: effectiveLocale });
48 } catch (error) {
49 // Fallback to en-US if locale is invalid
50 return dateTime.toLocaleString(options, { locale: "en-US" });
51 }
52 }, [dateString, options, timezone, language, hasPageLoaded]);
53}