如何在 Next.js (Pages Router) v16 中为不同区域格式化日期

以区域特定的格式显示日期

问题

日期没有统一的书写格式。序列 10/12/2025 在美国表示 10 月 12 日,但在英国表示 12 月 10 日。书写 "Oct 12, 2025" 假设了英语月份名称和特定的排序规则。每个地区对日期格式都有自己的期望,包括组件的顺序、分隔符,以及月份名称是以文字还是数字形式出现。

以单一格式显示日期会让应用程序对该格式区域以外的用户显得陌生。用户可能会误解日期或觉得难以阅读。对一个受众清晰的日期可能对另一个受众来说是令人困惑或模棱两可的。

解决方案

根据用户的区域设置格式化日期,将日期值转换为符合区域规则的字符串,包括顺序、分隔符和月份名称。浏览器的 Intl.DateTimeFormat API 在提供区域设置和可选的格式化偏好时,会自动处理区域特定的格式化规则。

react-intl 提供了封装此 API 的组件和钩子,并与应用程序的区域设置上下文集成。通过将日期值和格式化选项传递给这些工具,日期可以以用户的语言和地区既清晰又自然的方式呈现。

步骤

1. 使用 FormattedDate 创建日期格式化组件

FormattedDate 组件接受符合 Intl.DateTimeFormatOptions 的日期值和格式化选项。创建一个显示具有所需详细级别的日期的组件。

import { FormattedDate } from "react-intl";

interface EventDateProps {
  date: Date;
}

export function EventDate({ date }: EventDateProps) {
  return (
    <time dateTime={date.toISOString()}>
      <FormattedDate value={date} year="numeric" month="long" day="numeric" />
    </time>
  );
}

FormattedDate 从 IntlProvider 上下文中读取区域设置,并相应地格式化日期。对于 en-US,组件将显示 "October 12, 2025",而对于 en-GB,将显示 "12 October 2025"。

2. 使用 useIntl 命令式格式化日期

对于无法使用 React 组件的情况,例如设置文本属性,可以使用 useIntl 钩子来访问 formatDate 方法。

import { useIntl } from "react-intl";

interface ArticleImageProps {
  src: string;
  publishedAt: Date;
}

export function ArticleImage({ src, publishedAt }: ArticleImageProps) {
  const intl = useIntl();

  const formattedDate = intl.formatDate(publishedAt, {
    year: "numeric",
    month: "short",
    day: "numeric",
  });

  return <img src={src} alt={`文章发布于 ${formattedDate}`} />;
}

formatDate 方法返回一个格式化的日期字符串,并接受符合 DateTimeFormatOptions 的选项。这种方法适用于属性、服务端代码或对性能要求较高的场景。

3. 使用标准选项调整格式化细节

通过指定 year、month、day 和 weekday 等选项,以及 "numeric"、"2-digit"、"long"、"short" 或 "narrow" 等值,控制日期组件的显示方式。

import { FormattedDate } from "react-intl";

export function FullEventDate({ date }: { date: Date }) {
  return (
    <FormattedDate
      value={date}
      weekday="long"
      year="numeric"
      month="long"
      day="numeric"
    />
  );
}

export function CompactDate({ date }: { date: Date }) {
  return (
    <FormattedDate value={date} year="2-digit" month="2-digit" day="2-digit" />
  );
}

FullEventDate 组件将在 en-US 中渲染为 "Monday, October 12, 2025",在 de-DE 中渲染为 "Montag, 12. Oktober 2025"。CompactDate 组件生成适合表格或紧凑布局的简短数字格式。

4. 创建可重用的日期格式化助手

为了在多个页面中实现一致的日期格式化,可以创建一个封装了常用格式化模式的助手函数。

import { useIntl } from "react-intl";

export function useDateFormatters() {
  const intl = useIntl();

  return {
    formatShortDate: (date: Date) =>
      intl.formatDate(date, {
        year: "numeric",
        month: "short",
        day: "numeric",
      }),

    formatLongDate: (date: Date) =>
      intl.formatDate(date, {
        weekday: "long",
        year: "numeric",
        month: "long",
        day: "numeric",
      }),

    formatMonthYear: (date: Date) =>
      intl.formatDate(date, {
        year: "numeric",
        month: "long",
      }),
  };
}

在任何页面组件中导入并使用此钩子,可以在不重复选项的情况下一致地格式化日期。

5. 在页面组件中使用辅助工具

在 Next.js 页面中应用日期格式化辅助工具,以显示符合区域语言环境的日期。

import { GetStaticProps } from "next";
import { useDateFormatters } from "../lib/useDateFormatters";

interface Post {
  title: string;
  publishedAt: string;
  content: string;
}

interface BlogPostPageProps {
  post: Post;
}

export default function BlogPostPage({ post }: BlogPostPageProps) {
  const { formatLongDate } = useDateFormatters();
  const publishedDate = new Date(post.publishedAt);

  return (
    <article>
      <h1>{post.title}</h1>
      <p>发布时间:{formatLongDate(publishedDate)}</p>
      <div>{post.content}</div>
    </article>
  );
}

export const getStaticProps: GetStaticProps = async () => {
  const post = {
    title: "理解国际化",
    publishedAt: "2025-10-12T00:00:00Z",
    content: "内容在这里...",
  };

  return {
    props: { post },
  };
};

该页面将以用户的区域格式显示发布日期,使日期对任何地区的读者都清晰易懂。