Files
maternal-app/maternal-web/hooks/useLocalizedDate.ts

150 lines
4.5 KiB
TypeScript

import { useTranslation } from './useTranslation';
import { useAuth } from '@/lib/auth/AuthContext';
import {
format as dateFnsFormat,
formatDistanceToNow as dateFnsFormatDistanceToNow,
formatDistance as dateFnsFormatDistance,
formatRelative as dateFnsFormatRelative,
} from 'date-fns';
import { formatInTimeZone } from 'date-fns-tz';
import { enUS, es, fr, ptBR, zhCN, type Locale } from 'date-fns/locale';
/**
* Map of i18next language codes to date-fns locales
*/
const localeMap: Record<string, Locale> = {
'en': enUS,
'en-US': enUS,
'es': es,
'es-ES': es,
'fr': fr,
'fr-FR': fr,
'pt': ptBR,
'pt-BR': ptBR,
'zh': zhCN,
'zh-CN': zhCN,
};
/**
* Custom hook for localized date formatting using date-fns
* Automatically applies the correct locale, timezone, and time format based on user preferences
*/
export function useLocalizedDate() {
const { language } = useTranslation();
const { user } = useAuth();
// Get the locale for the current language, fallback to enUS
const locale = localeMap[language] || enUS;
// Get timezone from user preferences, fallback to UTC or browser timezone
const timezone = user?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
// Get time format preference (12h or 24h)
const timeFormat = user?.preferences?.timeFormat || '12h';
/**
* Format a date using date-fns with the current locale and timezone
* @param date - The date to format
* @param formatStr - The format string (e.g., 'PPP', 'MM/dd/yyyy', 'p' for time)
* @param options - Optional formatting options
*/
const format = (
date: Date | number | string,
formatStr: string,
options?: { useTimezone?: boolean }
): string => {
const dateObj = typeof date === 'string' ? new Date(date) : date;
// Replace time format tokens with 12h/24h based on preference
let adjustedFormat = formatStr;
if (timeFormat === '24h') {
// Convert 12h format to 24h format
adjustedFormat = formatStr.replace(/h:mm a/gi, 'HH:mm').replace(/p/g, 'HH:mm');
}
// Use timezone if requested
if (options?.useTimezone && timezone) {
return formatInTimeZone(dateObj, timezone, adjustedFormat, { locale });
}
return dateFnsFormat(dateObj, adjustedFormat, { locale });
};
/**
* Format a date as a distance to now (e.g., "2 hours ago")
* @param date - The date to format
* @param options - Optional options for formatDistanceToNow
*/
const formatDistanceToNow = (
date: Date | number | string,
options?: { addSuffix?: boolean; includeSeconds?: boolean }
): string => {
const dateObj = typeof date === 'string' ? new Date(date) : date;
return dateFnsFormatDistanceToNow(dateObj, { ...options, locale });
};
/**
* Format the distance between two dates
* @param date - The later date
* @param baseDate - The earlier date
* @param options - Optional options for formatDistance
*/
const formatDistance = (
date: Date | number,
baseDate: Date | number,
options?: { addSuffix?: boolean; includeSeconds?: boolean }
): string => {
return dateFnsFormatDistance(date, baseDate, { ...options, locale });
};
/**
* Format a date relative to now (e.g., "today at 5:00 PM")
* @param date - The date to format
* @param baseDate - Optional base date (defaults to now)
*/
const formatRelative = (
date: Date | number,
baseDate?: Date | number
): string => {
return dateFnsFormatRelative(date, baseDate || new Date(), { locale });
};
/**
* Format a time string with the user's preferred time format
* @param date - The date/time to format
*/
const formatTime = (date: Date | number | string): string => {
const dateObj = typeof date === 'string' ? new Date(date) : date;
const formatStr = timeFormat === '24h' ? 'HH:mm' : 'h:mm a';
return dateFnsFormat(dateObj, formatStr, { locale });
};
/**
* Format a date and time with the user's timezone and time format
* @param date - The date/time to format
*/
const formatDateTime = (date: Date | number | string): string => {
const dateObj = typeof date === 'string' ? new Date(date) : date;
const timeFormatStr = timeFormat === '24h' ? 'HH:mm' : 'h:mm a';
const fullFormat = `PPP ${timeFormatStr}`;
if (timezone) {
return formatInTimeZone(dateObj, timezone, fullFormat, { locale });
}
return dateFnsFormat(dateObj, fullFormat, { locale });
};
return {
format,
formatDistanceToNow,
formatDistance,
formatRelative,
formatTime,
formatDateTime,
locale,
timezone,
timeFormat,
};
}