date-fns vs dayjs vs moment vs luxon
JavaScript 日期时间处理库选型指南
date-fnsdayjsmomentluxon类似的npm包:
JavaScript 日期时间处理库选型指南

date-fnsdayjsluxonmoment 都是 JavaScript 生态中用于处理日期和时间的流行库,提供了日期解析、格式化、计算、国际化以及时区转换等功能。moment 曾长期占据主导地位,但因其可变状态设计、较大的 bundle 体积和缺乏 tree-shaking 支持,官方已于 2020 年宣布进入维护模式,不再推荐用于新项目。date-fns 采用函数式编程范式,基于原生 Date 对象,强调不可变性和模块化;dayjs 提供类似 moment 的链式 API,但内部实现为不可变对象,且核心体积极小;luxon 则由 Moment 团队成员开发,深度集成浏览器 Intl API,专注于不可变性和强大的时区处理能力。这些库各有侧重,适用于不同的工程场景和架构需求。

npm下载趋势
3 年
GitHub Stars 排名
统计详情
npm包名称
下载量
Stars
大小
Issues
发布时间
License
date-fns45,101,75536,46722.6 MB9111 年前MIT
dayjs36,519,07948,542679 kB1,1964 个月前MIT
moment28,553,49148,0644.35 MB2902 年前MIT
luxon21,096,46216,3554.59 MB1945 个月前MIT

JavaScript 日期时间库深度对比:date-fns、dayjs、luxon 与 moment

在现代前端开发中,处理日期和时间看似简单,实则充满陷阱 —— 时区转换、格式化、国际化、不可变性、性能优化等问题层出不穷。date-fnsdayjsluxonmoment 是四个主流的 JavaScript 日期库,但它们的设计哲学、API 风格和适用场景大不相同。本文将从真实工程角度出发,深入比较它们的核心能力。

⚠️ 重要提示:根据 moment 官方文档,该库已进入维护模式("maintenance mode"),不再推荐用于新项目。官方明确建议开发者评估 date-fnsdayjsluxon 等替代方案。

📅 核心设计理念:函数式 vs 面向对象 vs 不可变对象

不同库对“如何表示时间”有根本分歧,这直接影响代码风格和可维护性。

date-fns 采用纯函数式设计 —— 所有操作都返回新的 Date 对象,原始输入不变。它像 Lodash 一样提供大量独立函数,按需导入。

// date-fns: 纯函数 + 原生 Date
import { addDays, format } from 'date-fns';

const today = new Date();
const tomorrow = addDays(today, 1); // 返回新 Date 实例
const formatted = format(tomorrow, 'yyyy-MM-dd');

dayjs 模仿 moment 的链式 API,但内部使用不可变对象。每次调用方法都会返回新实例,避免意外修改。

// dayjs: 链式调用 + 不可变
import dayjs from 'dayjs';

const tomorrow = dayjs().add(1, 'day');
const formatted = tomorrow.format('YYYY-MM-DD');

luxon 基于 Immutable.js 思想,所有 DateTime 对象都是不可变的,并深度集成 Intl API 处理时区和本地化。

// luxon: 不可变 + 强大的时区支持
import { DateTime } from 'luxon';

const dt = DateTime.now().plus({ days: 1 });
const formatted = dt.toFormat('yyyy-MM-dd');

moment(已弃用)使用可变对象设计 —— 调用 .add() 会直接修改原对象,容易引发隐蔽 bug。

// moment: 可变对象(不推荐)
import moment from 'moment';

const now = moment();
now.add(1, 'day'); // now 被直接修改!

🌍 时区处理:基础支持 vs 专业级能力

时区是日期库最复杂的部分。各库能力差异显著。

date-fns 本身不处理时区,但通过 date-fns-tz 插件提供有限支持。适合简单场景。

// date-fns + date-fns-tz
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz';

const utcDate = zonedTimeToUtc('2023-06-01 12:00', 'Asia/Shanghai');
const shanghaiTime = utcToZonedTime(utcDate, 'Asia/Shanghai');
format(shanghaiTime, 'yyyy-MM-dd HH:mm', { timeZone: 'Asia/Shanghai' });

dayjs 通过插件 timezone 支持时区,但功能较基础,依赖外部数据(如 Intl)。

// dayjs + timezone plugin
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
dayjs.extend(timezone);

const shanghai = dayjs().tz('Asia/Shanghai').format('YYYY-MM-DD HH:mm');

luxon 内置强大时区引擎,直接利用浏览器 Intl API,无需额外插件。支持 IANA 时区、夏令时自动处理。

// luxon: 原生时区支持
import { DateTime } from 'luxon';

const dt = DateTime.fromISO('2023-06-01T12:00:00', { zone: 'Asia/Shanghai' });
const inNY = dt.setZone('America/New_York');
console.log(inNY.toISO()); // 自动转换并保留时区信息

moment 需要单独引入 moment-timezone 包才能处理时区,且体积庞大。

// moment + moment-timezone(不推荐)
import moment from 'moment-timezone';

const shanghai = moment.tz('2023-06-01 12:00', 'Asia/Shanghai');

📦 国际化(i18n):开箱即用 vs 按需加载

多语言支持对全球化应用至关重要。

date-fns 提供 70+ 语言的 locale 文件,可 tree-shake,只打包用到的语言。

// date-fns i18n
import { format } from 'date-fns';
import { zhCN } from 'date-fns/locale';

format(new Date(), 'PPPP', { locale: zhCN }); // "2023年6月1日星期四"

dayjs 通过 locale 插件支持多语言,同样可按需引入。

// dayjs i18n
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');

dayjs().format('dddd, MMMM D, YYYY'); // "星期四, 六月 1, 2023"

luxon 直接调用浏览器 Intl.DateTimeFormat,自动适配系统语言,无需额外包。但格式控制不如其他库灵活。

// luxon i18n
import { DateTime } from 'luxon';

// 自动使用浏览器语言
DateTime.now().toLocaleString(DateTime.DATE_FULL); 
// 中文环境输出:"2023年6月1日"

moment 内置多语言支持,但所有 locale 都包含在主包中,无法 tree-shake,导致 bundle 体积膨胀。

⚙️ 格式化解析:灵活性与易用性权衡

格式化字符串语法各不相同,影响开发体验。

date-fns 使用类似 Unicode Technical Standard #35 的 tokens(如 yyyy, MM, dd),清晰直观。

format(new Date(), 'yyyy-MM-dd HH:mm:ss'); // "2023-06-01 14:30:00"

dayjs 模仿 moment 的格式语法(YYYY, MM, DD),老用户迁移成本低。

dayjs().format('YYYY-MM-DD HH:mm:ss'); // "2023-06-01 14:30:00"

luxon 使用自己的格式 tokens(yyyy, LL, dd),或推荐使用 toLocaleString() 避免自定义格式。

DateTime.now().toFormat('yyyy-MM-dd HH:mm:ss');

moment 使用 YYYY-MM-DD 风格,但因可变性问题,格式化时可能产生副作用。

🧩 插件生态与扩展性

date-fns 无插件系统,所有功能通过独立函数提供。扩展需自行封装。

dayjs 设计了轻量插件机制(.extend()),官方提供 timezone、customParseFormat 等插件。

import customParseFormat from 'dayjs/plugin/customParseFormat';
dayjs.extend(customParseFormat);

dayjs('2023/06/01', 'YYYY/MM/DD'); // 支持自定义解析格式

luxon 功能完整内置于核心,无需插件。通过设置全局配置(如 Settings.defaultLocale)调整行为。

moment 有丰富插件生态,但因项目停滞,新插件几乎不再更新。

🔁 日期计算与比较:API 一致性

常见操作如加减天数、比较大小等:

// 加 7 天
// date-fns
addDays(new Date(), 7);

// dayjs
dayjs().add(7, 'day');

// luxon
DateTime.now().plus({ days: 7 });

// moment (avoid)
moment().add(7, 'days');

// 比较两个日期
// date-fns
isAfter(date1, date2);

// dayjs
dayjs(date1).isAfter(date2);

// luxon
DateTime.fromJSDate(date1) > DateTime.fromJSDate(date2);

📊 总结:关键差异一览

特性date-fnsdayjsluxonmoment(已弃用)
设计范式函数式 + 原生 Date链式 + 不可变不可变对象 + Intl 深度集成可变对象(危险)
时区支持date-fns-tz 插件timezone 插件内置,强大moment-timezone
国际化按需引入 locale,可 tree-shake按需引入 locale自动使用浏览器 Intl内置所有 locale,无法摇树
Bundle 优化极佳(仅导入用到的函数)良好(核心小,插件按需)中等(功能完整但不可分割)差(全量打包)
学习曲线低(类似 Lodash)低(熟悉 moment 的开发者)中(需理解 Immutable 和 Intl)低(但有陷阱)
新项目推荐度✅✅✅✅✅✅✅✅✅(尤其涉及时区)❌(官方不推荐)

💡 选型建议

  • 追求极致轻量和 tree-shaking → 选 date-fns。适合内容型网站、营销页等对 bundle 敏感的场景。
  • 需要熟悉 moment 的 API 但想要现代化替代 → 选 dayjs。适合从 moment 迁移的项目或偏好链式调用的团队。
  • 应用涉及复杂时区、国际化或金融/航空等高精度时间场景 → 选 luxon。它的不可变性和 Intl 集成能避免大量边界 case 错误。
  • 任何新项目都不应选择 moment —— 官方已明确其生命周期结束,继续使用将带来长期维护风险。

最终,没有“最好”的库,只有“最合适”当前项目需求的工具。理解它们的设计取舍,才能做出明智的技术决策。

如何选择: date-fns vs dayjs vs moment vs luxon
  • date-fns:

    选择 date-fns 如果你追求极致的 bundle 体积优化和 tree-shaking 能力,偏好函数式编程风格,并且项目对时区支持要求不高(或可通过 date-fns-tz 插件满足)。它特别适合内容型网站、营销落地页等对加载性能敏感的场景,也适合喜欢 Lodash 风格工具库的团队。

  • dayjs:

    选择 dayjs 如果你或你的团队熟悉 moment 的链式 API,希望平滑迁移至现代化替代品,同时需要小巧的核心体积和按需加载的插件机制。它在保持简洁的同时提供了良好的时区和国际化支持,适合大多数常规 Web 应用,尤其是中后台管理系统。

  • moment:

    不要在新项目中选择 moment。根据其官方文档,该项目已进入维护模式,不再接受新功能,且存在可变状态、bundle 体积大、无法 tree-shaking 等固有问题。现有项目应制定迁移计划,评估迁移到 date-fnsdayjsluxon 的可行性。

  • luxon:

    选择 luxon 如果你的应用涉及复杂的时区逻辑(如全球协作、航班调度、金融交易)、需要深度依赖浏览器 Intl API 实现精准的本地化,或强调不可变数据流以避免状态污染。它由 Moment 团队核心成员开发,代表了日期处理的现代最佳实践,适合对时间精度和可靠性要求高的专业级应用。

date-fns的README

🔥️ NEW: date-fns v4.0 with first-class time zone support is out!

date-fns

date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in a browser & Node.js

👉 Documentation

👉 Blog


It's like Lodash for dates

  • It has 200+ functions for all occasions.
  • Modular: Pick what you need. Works with webpack, Browserify, or Rollup and also supports tree-shaking.
  • Native dates: Uses existing native type. It doesn't extend core objects for safety's sake.
  • Immutable & Pure: Built using pure functions and always returns a new date instance.
  • TypeScript: The library is 100% TypeScript with brand-new handcrafted types.
  • I18n: Dozens of locales. Include only what you need.
  • and many more benefits
import { compareAsc, format } from "date-fns";

format(new Date(2014, 1, 11), "yyyy-MM-dd");
//=> '2014-02-11'

const dates = [
  new Date(1995, 6, 2),
  new Date(1987, 1, 11),
  new Date(1989, 6, 10),
];
dates.sort(compareAsc);
//=> [
//   Wed Feb 11 1987 00:00:00,
//   Mon Jul 10 1989 00:00:00,
//   Sun Jul 02 1995 00:00:00
// ]

The library is available as an npm package. To install the package run:

npm install date-fns --save

Docs

See date-fns.org for more details, API, and other docs.


License

MIT © Sasha Koss