跳转到主要内容
技术

JavaScript 日期库对比 - 从 Day.js 到 Temporal API

为什么原生 Date 对象力不从心

JavaScript 内置的 Date 自 1995 年以来几乎没有变化,承载着那个时代的所有设计缺陷。月份从零开始计数,时区行为默认使用本地时间且令人困惑,Date 实例是可变的,在函数间共享时存在风险。实际应用很快就会触及 Date 的局限性,转而使用第三方库。

在各种库之间做选择取决于五个维度:包体积、API 设计、时区支持、国际化和活跃维护。在前端,包体积占主导地位,因为每一千字节都影响加载时间;而在后端,正确性和可读性往往更为重要。一个项目在客户端和服务端使用不同的库是很常见的。

Moment.js - 处于维护模式的老将

Moment.js 从 2011 年发布到 2010 年代末期一直主导着 JavaScript 日期处理领域。其丰富的 API、流畅的方法链和出色的国际化使其成为显而易见的选择。2020 年,维护者自己宣布该项目为遗留库,并建议不要在新代码中使用。原因是可变性、无法 tree-shaking,以及约 290 KB 的包体积对现代 Web 应用的拖累。

全面迁移很困难,因为 Moment 的 API 无处不在。大多数团队采用分阶段方式:所有新代码使用现代库,现有的 Moment 用法在自然接触点时逐步替换。Moment 继续接收安全补丁,但其功能集实际上已经冻结。

Day.js - 轻量级的 Moment 替代品

Day.js 于 2018 年发布,明确目标是以 Moment 体积的零头提供兼容 Moment 的 API。核心包约 7 KB,不到 Moment 的四十分之一。时区、相对时间和多语言支持作为可选插件提供,应用只需为实际使用的功能付费。dayjs(“2026-05-18”).add(1, “day”).format(“YYYY-MM-DD”) 的写法与 Moment 完全一致,使其成为最容易的迁移目标。

Day.js 并未在功能上完全复制 Moment,因此在正式采用之前请检查高级 API 的兼容性。其时区插件在底层使用 Intl.DateTimeFormat,这意味着在 ICU 数据精简的 Node.js 构建中可能会失败。提前了解这一限制可以在部署到最小运行时环境时节省调试时间。

date-fns - 函数式、可 tree-shake 的选择

date-fns 采用了不同的设计哲学,使用函数式 API:每个操作都是接收 Date 并返回新 Date 的顶层函数。没有专有的类,只有标准 Date 实例在纯函数之间流动。Tree-shaking 效果完美,一个只使用 format 和 addDays 的项目只需打包几千字节。由于参数类型明确,TypeScript 类型推断非常出色。

代价是 API 风格与 Moment 风格的链式调用截然不同。format(date, “yyyy-MM-dd”) 对习惯了 date.format(...) 的开发者来说很陌生。时区操作需要单独的 date-fns-tz 包,增加约 12 KB 但提供完整的 IANA 支持。许多新项目正是因为包体积优势而选择 date-fns。

Luxon - 为严肃的时区工作而生

Luxon 由 Moment 的维护者创建,旨在解决原始库的架构缺陷。它是不可变的、现代的,围绕完整的 IANA 时区支持而设计。DateTime.fromISO(“2026-05-18T09:00”, { zone: “Asia/Tokyo” }) 一次调用即可创建完整的带时区日期时间。对于时区处理是核心需求的应用,其夏令时处理、日历运算和格式化堪称一流。

Luxon 高度依赖 Intl,这意味着较老的浏览器或精简的 Node.js 构建可能需要额外的 polyfill。包体积大于 date-fns 但小于 Moment,对于调度或国际物流等时区密集型应用,其带时区日期时间 API 的可读性通常可以证明其体积的合理性。

Temporal - 标准化的未来

Temporal 提案目前处于 ECMAScript 流程的 Stage 3 阶段,正逐步登陆各浏览器。它引入了一系列不可变类型(PlainDate、PlainTime、PlainDateTime、ZonedDateTime、Instant),清晰地分离关注点。Temporal 还将公历以外的日历系统(包括日本年号和希伯来历)作为一等语言特性支持。

截至 2026 年中期,Temporal 尚未普遍可用,生产环境使用需要 @js-temporal/polyfill。一旦浏览器提供原生实现,第三方库在许多场景下将变为可选。新项目现在就可以使用 Temporal 加 polyfill 起步,日后去除 polyfill,在几年内摆脱对 Day.js、Luxon 和 date-fns 的依赖。

如何选择合适的工具

如果你的需求仅限于格式化和简单的算术运算,原生 Date 加 Intl.DateTimeFormat 就够用了。对于注重包体积的前端项目,推荐 date-fns 或 Day.js。对于有大量时区处理或多语言渲染需求的后端服务,Luxon 的 API 难以超越。对于有长远规划的全新项目,Temporal 加 polyfill 是最具前瞻性的选择。

更大的错误是因为惯性而继续使用 Moment。每推迟一年迁移,安全补丁和依赖升级就会不断累积,最终的重写难度也越来越大。新代码应该立即采用现代库,旧的 Moment 用法逐步清理。大多数团队发现 90 天的迁移计划足以停止新增 Moment 依赖,即使完全移除需要更长时间。

XB!LINE

这篇文章对您有帮助吗?

相关文章