news 2026/9/16 18:10:34

Carbon Utilities 日期时间格式化指南:基于 Intl API 的 dateTimeFormat 封装详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Carbon Utilities 日期时间格式化指南:基于 Intl API 的 dateTimeFormat 封装详解

Carbon Utilities 日期时间格式化指南:基于 Intl API 的 dateTimeFormat 封装详解

【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon

导读

本文聚焦 IBM Carbon Design System 中@carbon/utilities包提供的dateTimeFormat工具,一个对原生Intl.RelativeTimeFormatIntl.DateTimeFormatIntl.DurationFormat的轻量封装。无论你需要在产品界面中展示"3 minutes ago"这类相对时间、Apr 4, 2024这类绝对日期,还是1h 23m 45s这类时长,dateTimeFormat都能以统一、可本地化的 API 在几行代码内完成。读完本文,你将掌握其全部三个子模块(relative / absolute / duration)的调用方式、样式参数与默认值、时区处理,以及底层实现原理与测试验证方法。

说明:本工具的完整用法遵循 Carbon for IBM Products 站点的 Date and time guidance 设计指南(仓库内开发者手册亦对日期时间相关规范有整体约束),本文重点讲解 API 本身。

dateTimeFormat 是什么

dateTimeFormat@carbon/utilities(package.json)中提供的一组日期时间格式化封装。它本身不重复造轮子,而是围绕三个 ECMAScript Intl API 做薄封装:

  • Intl.RelativeTimeFormat—— 相对时间(如"3 minutes ago")
  • Intl.DateTimeFormat—— 绝对日期时间(如"Apr 4, 2024 at 3:47 PM")
  • Intl.DurationFormat—— 时长(如"1h 23m 45s")

在 index.ts 中可以看到它的聚合导出方式:

import * as relative from './relative'; import * as absolute from './absolute'; import * as duration from './duration'; export const dateTimeFormat = { relative, absolute, duration, };

dateTimeFormat是一个命名空间对象,由三个子模块组成。而 src/index.ts 再将其作为@carbon/utilities包的公开 API 导出,因此可以这样引入:

import { dateTimeFormat } from '@carbon/utilities';

所有格式化结果都由底层 Intl API 按locale生成本地化文本,因此天然支持多语言,无需手写任何翻译映射。

相对时间:dateTimeFormat.relative

relative模块对应Intl.RelativeTimeFormat,用于将某个时间点相对"现在"进行人性化描述。

样式参数

  • 支持的样式:"long" | "short" | "narrow"
  • 默认样式:"long"

基本用法

import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.relative.format(timestamp); // 3 minutes ago dateTimeFormat.relative.format(timestamp, { locale: 'de-DE' }); // vor 3 Minuten dateTimeFormat.relative.format(timestamp, { style: 'short' }); // 3 min. ago

可以看到,传入locale即可获得本地化文案,传入style可切换详细程度(long完整、short缩写、narrow最紧凑)。

底层实现:单位自动推导

从 relative.ts 的源码可以看到,format接收Date | number,内部完成"时间差 → 单位选择"的自动换算:

const seconds = Math.floor((now - d.getTime()) / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); const weeks = Math.floor(days / 7); const months = Math.floor(weeks / 4); const years = Math.floor(days / 365);

随后按阶梯选择最合适的单位:

  • 差值绝对值小于 60 秒 → 以numeric: 'auto'Intl.RelativeTimeFormat输出(例如"now")
  • 小于 60 分钟 → 输出分钟
  • 小于 24 小时 → 输出小时
  • 小于 7 天 → 输出天
  • 小于 4 周 → 输出周
  • 小于 365 天 → 输出月
  • 其余 → 输出年

注意,months是按"4 周"估算的,years按 365 天估算,因此这是面向展示的精简近似换算,并非日历级的精确月/年差计算。测试用例 relative-test.js 覆盖了从 1 分钟到 2 年、以及未来时间(+)与过去时间(-)两个方向的全部单位分支,例如['hours', 23, 60 * 60]验证 23 小时内仍归入 hours 单位。

绝对时间:dateTimeFormat.absolute

absolute模块对应Intl.DateTimeFormat,提供四种粒度的格式化方法:formatTimeformatDateformatformatRange

formatTime:仅时间

  • 支持的样式:"full" | "long" | "medium" | "short"
  • 默认样式:"short"
import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.formatTime(timestamp); // 3:47 PM dateTimeFormat.absolute.formatTime(timestamp, { locale: 'de-DE' }); // 15:47 dateTimeFormat.absolute.formatTime(timestamp, { style: 'long' }); // 3:47:12 PM

底层实现(absolute.ts)直接构造Intl.DateTimeFormat并传入timeStyle

const dtf = new Intl.DateTimeFormat(options?.locale, { timeStyle: options?.style ?? 'short', timeZone: options?.timeZone, }); return dtf.format(date);

formatDate:仅日期

  • 支持的样式:"full" | "long" | "medium" | "short"
  • 默认样式:"medium"
import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.formatDate(timestamp); // Apr 4, 2024 dateTimeFormat.absolute.formatDate(timestamp, { locale: 'de-DE' }); // 04.04.2024 dateTimeFormat.absolute.formatDate(timestamp, { style: 'full' }); // Thursday, April 4, 2024

format:日期 + 时间

format是使用频率最高的方法,支持同时输出日期与时间,并额外提供"tooltip"快捷样式。

  • 支持的样式:"full" | "long" | "medium" | "short" | "tooltip"
    • "tooltip"timeStyle: "long", dateStyle: "full"的简写
  • 支持的时间样式:"full" | "long" | "medium" | "short"
  • 支持的日期样式:"full" | "long" | "medium" | "short"
  • 默认时间样式:"short"
  • 默认日期样式:"medium"
import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.format(timestamp); // Apr 4, 2024 at 3:47 PM dateTimeFormat.absolute.format(timestamp, { locale: 'de-DE' }); // 04.04.2024, 15:47 dateTimeFormat.absolute.format(timestamp, { timeStyle: 'medium', dateStyle: 'short', }); // April 4, 2024 at 3:47:12 PM dateTimeFormat.absolute.format(timestamp, { style: 'short' }); // 4/4/24, 3:47 PM dateTimeFormat.absolute.format(timestamp, { style: 'tooltip' }); // Thursday, April 4, 2024 at 3:47:12 PM GMT+2

从源码可以看到tooltip是如何展开的,以及各参数的优先级逻辑(absolute.ts):

const timeStyle = options?.timeStyle ?? (options?.style === 'tooltip' ? 'long' : options?.style) ?? 'short'; const dateStyle = options?.dateStyle ?? (options?.style === 'tooltip' ? 'full' : options?.style) ?? 'medium';

即:显式传入的timeStyle/dateStyle优先级最高;其次是由style展开(其中'tooltip'映射为long+full);最后落到默认值short/medium

formatRange:区间

formatRange用于展示一段起止时间区间,底层调用Intl.DateTimeFormat.prototype.formatRange

  • 支持的样式:"full" | "long" | "medium" | "short"
  • 支持的时间样式:"full" | "long" | "medium" | "short" | null
  • 支持的日期样式:"full" | "long" | "medium" | "short" | null
  • 默认时间样式:"short"
  • 默认日期样式:"medium"
import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.formatRange(startDate, endDate); // Apr 4, 2024, 3:47 PM – Apr 25, 2024, 4:29 PM dateTimeFormat.absolute.formatRange(startDate, endDate, { locale: 'de-DE' }); // 04.04.2024, 15:47 – 25.04.2024, 16:29 dateTimeFormat.absolute.formatRange(startDate, endDate, { timeStyle: 'medium', dateStyle: 'short', }); // 4/4/24, 3:47:12 PM – 4/25/24, 4:29:38 PM dateTimeFormat.absolute.formatRange(startDate, endDate, { style: 'short' }); // 4/4/24, 3:47 PM – 4/25/24, 4:29 PM
仅日期区间

timeStyle设为null即可省略时间,只展示日期区间:

import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.formatRange(startDate, endDate, { timeStyle: null }); // Apr 28, 2016 – Jul 1, 2018
仅时间区间

dateStyle设为null可以只展示时间,但有一个重要前提:起止日期必须是同一天,否则省略日期会产生歧义。

import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.formatRange(startDate, sameDayEndDate, { dateStyle: null, }); // 3:47 – 4:29 PM

从 absolute.ts 源码可见,null会被转换为undefined传入Intl.DateTimeFormat,从而让该维度不参与渲染:

const timeStyle = options?.timeStyle === null ? undefined : (options?.timeStyle ?? options?.style ?? 'short'); const dateStyle = options?.dateStyle === null ? undefined : (options?.dateStyle ?? options?.style ?? 'medium');

absolute-test.js(absolute-test.js)中用sameDayEndDate(与startDate同一天)专门验证了dateStyle: null这一分支,且对全部四种样式 × 时区组合做了与原生Intl.DateTimeFormat输出逐字一致的断言。

时区(timeZone)

absolute的所有方法都支持可选属性timeZone,例如你想展示 UTC 时间而非本地时区:

import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.absolute.format(timestamp); // Apr 4, 2024 at 3:47 PM dateTimeFormat.absolute.format(timestamp, { timeZone: 'UTC' }); // Apr 4, 2024 at 10:47 PM

时区选项遵循 ECMAScript 2026 Internationalization API Specification(Intl.DateTimeFormattimeZone取值,如'UTC''Asia/Tokyo'等 IANA 时区名)。测试中即以UTCAsia/Tokyo两组时区对formatTimeformatDateformatformatRange全部进行了验证。

时长:dateTimeFormat.duration

duration模块对应Intl.DurationFormat,用于格式化时间段(如视频时长、任务耗时)。

  • 支持的样式:"long" | "short" | "narrow" | "digital"
  • 默认样式:"narrow"
import { dateTimeFormat } from '@carbon/utilities'; dateTimeFormat.duration.format(time); // 1h 23m 45s dateTimeFormat.duration.format(time, { locale: 'de-DE' }); // 1h, 23 Min. und 45 Sek. dateTimeFormat.duration.format(time, { style: 'long' }); // 1 hour, 23 minutes, 45 seconds

输入是一个以Intl.DurationFormatUnit为键的部分记录(duration.ts),例如{ hours: 1, minutes: 23, seconds: 45 },允许只传入需要的字段。默认样式"narrow"对应最紧凑的输出(1h 23m 45s),"digital"对应如1:23:45的数字风格,"long"输出完整单词。

duration-test.js 验证了"未传 style 时默认等于narrow",并对四种样式逐一与原生Intl.DurationFormat输出比对。

方法签名速查

子模块方法输入类型可选参数默认值
relativeformat(date)Date \| numberlocale,style: 'long' \| 'short' \| 'narrow'style: 'long'
absoluteformatTime(date)Date \| numberlocale,style,timeZonestyle: 'short'
absoluteformatDate(date)Date \| numberlocale,style,timeZonestyle: 'medium'
absoluteformat(date)Date \| numberlocale,style(含'tooltip'),timeStyle,dateStyle,timeZonetimeStyle: 'short',dateStyle: 'medium'
absoluteformatRange(start, end)Date \| numberlocale,style,timeStyle(可为null),dateStyle(可为null),timeZonetimeStyle: 'short',dateStyle: 'medium'
durationformat(duration)Partial<Record<Intl.DurationFormatUnit, number>>locale,style: 'long' \| 'short' \| 'narrow' \| 'digital'style: 'narrow'

所有date参数均接受Date对象或时间戳数字(Date \| number);locale遵循 BCP 47 语言标签(如'en-US''de-DE');所有输出结果完全交由浏览器或 Node.js 运行时中的 Intl 实现生成,因此无需引入任何额外的 i18n 依赖。

测试与正确性保障

仓库为每个子模块都提供了独立的测试文件,采用"与原生 Intl 输出逐字比对"的策略来保证封装行为的正确性:

  • relative-test.js:覆盖三种样式,对过去/未来两个方向、从 30 秒到 2 年的 12 组单位 × 数量组合进行断言;
  • absolute-test.js:对formatTime/formatDate/format/formatRange的每种样式、timeStyle/dateStyle手动组合、null省略分支以及UTCAsia/Tokyo时区,全部与直接构造的Intl.DateTimeFormat输出比对;
  • duration-test.js:验证默认样式为narrow,并对longshortnarrowdigital四种样式逐一比对。

由于该测试策略不依赖固定字符串,而是实时计算期望值,即使运行环境(Node/浏览器)的 ICU 数据或 Intl 实现版本不同,测试依然成立——这恰好也说明dateTimeFormat的输出是"运行时 Intl 能力"的直接反映。

使用建议与注意事项

  • 相对时间的单位近似relative中月按 4 周、年按 365 天折算,适合"几分钟前 / 上个月 / 去年"这类展示场景;若需要精确的日历月差,请自行计算或改用其他方案。
  • 仅时间区间的限制formatRangedateStyle: null仅适用于起止同日,跨日时必须保留日期,否则语义不完整。
  • 时区语义timeZone只在absolute系列方法上可用(relativeduration与本地时间无关,不提供该参数)。
  • 运行环境要求Intl.DurationFormat是较新的 API,请确认目标运行环境(Node 版本 / 浏览器版本)已支持;Intl.RelativeTimeFormatIntl.DateTimeFormattimeStyle/dateStyle/formatRange在现代环境中支持良好。
  • 零成本接入:工具本身仅做参数透传与默认值处理,不携带任何格式化数据或翻译表,包体积开销极小。

以上就是@carbon/utilitiesdateTimeFormat的完整用法。在 Carbon 相关的 React、Web Components 等产品界面中,需要展示时间戳、时间区间或时长的任何位置,都可以直接引入它获得一致、可本地化、且贴合 Carbon 设计语言时间展示规范的输出。

【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 18:08:20

网上鲜花销售系统毕设实战:从数据库设计到Docker部署全流程

简介&#xff1a;这是一套基于Django与Vue的网上鲜花销售系统毕业设计资源&#xff0c;面向计算机相关专业学生、开发者及小微商家&#xff0c;用于完成课程设计、毕业设计或快速搭建在线花店。系统覆盖鲜花展示、购物车、订单管理、用户管理等核心模块&#xff0c;并包含用户注…

作者头像 李华
网站建设 2026/9/16 18:07:27

自研轻量级桌面CRM:基于Electron与SQLite的实践与避坑指南

做CRM系统这事&#xff0c;我一开始是拒绝的。市面上的CRM工具我前后试了不下十款&#xff0c;要么功能堆得像瑞士军刀&#xff0c;实际用起来三分之二的功能一辈子碰不到&#xff1b;要么按坐席收费&#xff0c;团队还没扩到两位数&#xff0c;账单倒是先膨胀起来&#xff1b;…

作者头像 李华
网站建设 2026/9/16 18:07:06

MATLAB数字图像处理实战:从灰度变换到边缘检测的系统搭建

简介&#xff1a;一套面向通信工程、自动化、电子信息等计算机相关专业在校生与初学者的MATLAB数字图像处理项目资料包&#xff0c;可满足课程设计、毕业设计、大作业或项目初期演示等需求。内含经过完整测试的MATLAB脚本文件&#xff08;.m&#xff09;、图形界面文件&#xf…

作者头像 李华
网站建设 2026/9/16 18:06:59

NPS内网穿透协议原理与轻量级部署实战

1. NPS不是“网盘缩写”&#xff0c;而是内网穿透里少有人讲透的轻量级协议调度器很多人第一次看到NPS&#xff0c;下意识以为是Network Protocol Service或者某个国产网盘的缩写——其实它全称是NeoProxy Server&#xff0c;一个由国内开发者维护、专注解决“内网服务如何被公…

作者头像 李华