如何在 React Router v7 中为不同地区格式化日期
以地区特定的格式显示日期
问题
日期没有统一的书写格式。数字序列 10/12/2025 在美国表示 10 月 12 日,但在英国则表示 12 月 10 日。即使像 "Oct 12, 2025" 这样的格式,也假定了英文月份名称和特定的排列顺序,这可能与用户的习惯不符。当应用程序仅以一种固定格式显示日期时,对于采用不同习惯的用户来说会显得陌生,降低了清晰度和信任感。
不同地区在日期展示上有各自的规则,包括日、月、年顺序,分隔符的选择,以及月份名称是全称还是缩写。如果忽略这些习惯,用户就不得不在脑海中将日期转换为熟悉的格式,这会增加认知负担并提高误解的风险。
解决方案
根据用户的地区设置格式化日期值,将展示逻辑交给了解地区规范的国际化 API 处理。不要手动拼接日期字符串,而是将日期对象传递给格式化函数或组件,由其根据当前地区自动应用正确的顺序、分隔符和月份名称。
React-intl 提供了声明式组件和命令式方法,支持标准日期格式化选项,并生成符合地区规范的字符串。通过指定需要包含的日期部分及其详细程度,你可以控制输出样式,而库会自动处理地区差异。这种方式将日期格式化逻辑与展示组件分离,确保应用内的一致性。
步骤
1. 在组件中使用 FormattedDate 格式化日期
使用 FormattedDate 组件(来自 react-intl)可以根据本地化格式渲染日期。该组件接收一个日期值和与 Intl.DateTimeFormatOptions 对应的格式化选项。
import { FormattedDate } from "react-intl";
export default function EventCard({ event }) {
return (
<article>
<h2>{event.title}</h2>
<time>
<FormattedDate
value={event.date}
year="numeric"
month="long"
day="numeric"
/>
</time>
<p>{event.description}</p>
</article>
);
}
FormattedDate 组件会使用 formatDate 和 Intl.DateTimeFormat API,根据外围 IntlProvider 提供的本地化信息生成相应的字符串。通过 year、month 和 day 选项可以控制显示哪些部分以及它们的格式。
2. 使用 useIntl 命令式格式化日期
当你需要为属性、aria 标签或数据转换等非渲染场景获取格式化日期字符串时,可以使用 useIntl hook 获取 formatDate 方法。
import { useIntl } from "react-intl";
export default function EventList({ events }) {
const intl = useIntl();
return (
<ul>
{events.map((event) => {
const formattedDate = intl.formatDate(event.date, {
year: "numeric",
month: "short",
day: "numeric",
});
return (
<li key={event.id}>
<a
href={`/events/${event.id}`}
aria-label={`${event.title} on ${formattedDate}`}
>
{event.title}
</a>
</li>
);
})}
</ul>
);
}
formatDate 函数接收一个日期值和可选的 Intl.DateTimeFormatOptions,返回本地化格式的字符串。当需要将格式化后的日期嵌入其他字符串或作为 prop 值时,这非常有用。
3. 创建可复用的日期格式化辅助方法
将常用的日期格式化模式提取到一个辅助函数中,并封装 intl.formatDate,以确保整个应用内格式一致。
import { useIntl } from "react-intl";
export function useDateFormatter() {
const intl = useIntl();
return {
formatShortDate: (date: Date | number) =>
intl.formatDate(date, {
year: "numeric",
month: "numeric",
day: "numeric",
}),
formatLongDate: (date: Date | number) =>
intl.formatDate(date, {
year: "numeric",
month: "long",
day: "numeric",
}),
formatDateTime: (date: Date | number) =>
intl.formatDate(date, {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
}),
};
}
该 hook 集中管理格式化逻辑,便于在应用中统一应用日期样式。组件可根据所需的详细程度调用相应方法。
4. 在路由加载器中格式化日期
当数据加载过程中需要格式化日期时,可从请求中获取 locale,并使用 createIntl 在返回 loader 数据前完成日期格式化。
import type { Route } from "./+types/event";
import { createIntl, createIntlCache } from "react-intl";
const cache = createIntlCache();
export async function loader({ request }: Route.LoaderArgs) {
const url = new URL(request.url);
const locale = url.searchParams.get("locale") || "en";
const event = await fetchEvent();
const intl = createIntl({ locale, messages: {} }, cache);
return {
event,
formattedDate: intl.formatDate(event.date, {
year: "numeric",
month: "long",
day: "numeric",
}),
};
}
export default function Event({ loaderData }: Route.ComponentProps) {
return (
<article>
<h1>{loaderData.event.title}</h1>
<time>{loaderData.formattedDate}</time>
</article>
);
}
createIntl 函数会创建一个 intl 对象,用于在 React 组件之外格式化日期。这种方法可以在服务器端或静态生成期间预先格式化日期,从而减少客户端的处理工作。