date-fns、datejs、dayjs、luxon 和 moment 都是用于处理日期和时间的 JavaScript 库,但它们在设计理念、API 风格、功能覆盖和维护状态上存在显著差异。这些库帮助开发者解析、格式化、操作和国际化日期时间,避免直接使用原生 Date 对象带来的复杂性和跨浏览器问题。其中部分库已停止维护,而其他则持续演进以适应现代前端工程需求。
在现代前端开发中,处理日期和时间看似简单,实则充满陷阱 —— 时区、夏令时、本地化、不可变性等问题让原生 Date 对象难以胜任。本文将从真实工程视角,深入对比五个主流日期库的技术细节、适用场景和关键取舍。
首先必须明确:datejs 和 moment 已被官方标记为 deprecated,不应在新项目中使用。
datejs:npm 页面显示 “This package has been deprecated”,最后一次实质性更新在 2007 年,不支持现代 JavaScript 特性,且存在已知 bug(如月份索引错误)。moment:官网首页醒目提示 “Moment is in maintenance mode. We recommend using Luxon, Day.js, or date-fns instead.” 其设计缺陷(如可变对象、全局 locale)已无法通过迭代修复。因此,实际选型应聚焦于 date-fns、dayjs 和 luxon。下面我们将围绕这三个活跃库展开技术对比。
不同库的 API 风格直接影响代码可读性和维护成本。
date-fns 采用纯函数式设计:所有操作都是独立函数,输入日期输出新日期,天然不可变。
// date-fns: 函数式调用
import { addDays, format } from 'date-fns';
const today = new Date();
const nextWeek = addDays(today, 7);
const formatted = format(nextWeek, 'yyyy-MM-dd');
dayjs 模仿 Moment 的链式调用:通过包装 Date 对象提供方法链,但内部实现不可变(每次操作返回新实例)。
// dayjs: 链式调用
import dayjs from 'dayjs';
const formatted = dayjs().add(7, 'day').format('YYYY-MM-DD');
luxon 采用面向对象 + 静态工厂方法:使用 DateTime 类,强调不可变性和清晰的构造方式。
// luxon: 面向对象
import { DateTime } from 'luxon';
const formatted = DateTime.now().plus({ days: 7 }).toFormat('yyyy-MM-dd');
💡 工程建议:函数式风格(date-fns)更易测试和组合;链式(dayjs)对 Moment 用户迁移友好;Luxon 的对象模型在复杂时区操作中更直观。
处理多时区是国际化应用的核心挑战。三者能力差异显著。
date-fns 默认不支持时区:需额外安装 date-fns-tz 插件,并依赖浏览器 Intl API。
// date-fns + date-fns-tz
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz';
const utcDate = zonedTimeToUtc('2023-07-01 12:00:00', 'Asia/Shanghai');
const nyTime = utcToZonedTime(utcDate, 'America/New_York');
const formatted = format(nyTime, 'yyyy-MM-dd HH:mm', { timeZone: 'America/New_York' });
dayjs 通过插件支持时区:需引入 timezone 和 utc 插件,API 略显繁琐。
// dayjs + plugins
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault('Asia/Shanghai');
const nyTime = dayjs().tz('America/New_York');
const formatted = nyTime.format('YYYY-MM-DD HH:mm');
luxon 内置强大时区支持:直接利用浏览器 Intl API,无需额外插件,API 简洁。
// luxon: 内置时区
import { DateTime } from 'luxon';
const shanghai = DateTime.now().setZone('Asia/Shanghai');
const ny = shanghai.setZone('America/New_York');
const formatted = ny.toFormat('yyyy-MM-dd HH:mm');
💡 关键结论:若应用涉及多时区(如全球协作工具),Luxon 是最省心的选择;若仅需基础时区转换,date-fns 或 dayjs 配合插件也可满足。
现代前端对 bundle size 极其敏感,三者表现如下:
date-fns:每个函数独立导出,天然支持 tree-shaking。仅导入所需函数,最终体积最小。dayjs:核心极小(~2KB),但功能通过插件扩展。若使用多个插件,需手动管理导入,否则可能全量引入。luxon:单一体积较大(因内置 Intl 逻辑),但无法按需切割。适合功能需求全面的场景。示例:仅需格式化和加减天数
// date-fns: 仅打包两个函数
import { addDays, format } from 'date-fns';
// dayjs: 核心 + 可能需插件(如 advancedFormat)
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
dayjs.extend(advancedFormat);
// luxon: 整个 DateTime 模块被引入
import { DateTime } from 'luxon';
💡 选型建议:对体积极度敏感(如移动端 H5)→
date-fns或dayjs;功能复杂度高 →luxon的一体化方案更可靠。
日期操作若产生副作用,极易引发 bug。三者均保证不可变性,但实现方式不同。
date-fns:纯函数,输入输出隔离,无任何状态。dayjs:每次调用返回新 Dayjs 实例,原实例不变。luxon:DateTime 对象不可变,所有方法返回新实例。// 三者均保证以下操作不修改原值
const original = /* ... */;
const modified = original.add(1, 'day'); // original 不变
相比之下,已废弃的 moment 是可变的(虽有 .clone() 但易被忽略):
// moment (deprecated): 危险!
const m = moment();
m.add(1, 'day'); // m 本身被修改!
多语言应用需灵活切换日期格式。
date-fns:每个 locale 独立文件,按需导入。
import { format } from 'date-fns';
import { zhCN } from 'date-fns/locale';
format(new Date(), 'PPPP', { locale: zhCN });
dayjs:locale 作为插件加载,需手动设置。
import 'dayjs/locale/zh-cn';
dayjs.locale('zh-cn');
luxon:自动使用浏览器 locale,也可显式指定。
DateTime.now().setLocale('zh-CN').toFormat('D');
💡 注意:Luxon 依赖浏览器 Intl,若需支持老旧环境(如 IE11),需 polyfill;date-fns 和 dayjs 的 locale 数据内置于包中,兼容性更好。
date-fns —— 函数式风格契合现代框架,tree-shaking 效果最佳。dayjs —— 链式语法几乎一致,迁移成本最低。luxon —— 内置 Intl 集成,时区 API 最健壮。luxon 或 date-fns —— 两者均支持 Node,但 Luxon 的时区处理更省心。| 特性 | date-fns | dayjs | luxon |
|---|---|---|---|
| 维护状态 | ✅ 活跃 | ✅ 活跃 | ✅ 活跃 |
| API 风格 | 函数式 | 链式 | 面向对象 |
| Tree-shaking | ✅ 天然支持 | ⚠️ 需手动管理插件 | ❌ 不支持 |
| 时区支持 | ⚠️ 需插件 | ⚠️ 需插件 | ✅ 内置 |
| Intl 依赖 | 可选 | 可选 | 必需 |
| 不可变性 | ✅ 纯函数 | ✅ 新实例 | ✅ 新实例 |
| 迁移成本 | 中(新范式) | 低(类似 Moment) | 中(新概念) |
date-fns(轻量函数式)、dayjs(Moment 替代)、luxon(国际化重型)中三选一。datejs(已废弃)和 moment(维护模式)。日期处理是前端基础能力,选对工具能大幅减少 bug 和维护成本。理解每个库的设计哲学,才能在复杂业务中游刃有余。
选择 date-fns 如果你追求函数式编程风格、支持 tree-shaking 的模块化设计,以及轻量且可预测的 API。它适合对打包体积敏感的现代前端项目(如 React、Vue 应用),并需要良好的 TypeScript 支持和不可变数据操作。
选择 dayjs 如果你需要一个极小体积、Moment.js 风格 API 的替代品,同时希望保持链式调用习惯。它通过插件机制扩展功能,适合对 bundle size 极度敏感的项目(如移动端或微前端),且能接受手动加载插件带来的额外配置。
不要在新项目中使用 moment。官方文档已明确建议迁移到其他库,因其体积大、不可变性缺失、tree-shaking 不友好,且不再积极开发新功能。仅在维护旧项目或依赖其生态(如某些 UI 组件库)时保留。
不要在新项目中使用 datejs。该库自 2007 年后基本停止维护,npm 页面明确标注为 deprecated,且不兼容现代 JavaScript 环境(如 ES6+、TypeScript)。仅在维护遗留系统时可能遇到。
选择 luxon 如果你的应用重度依赖时区、国际化(Intl)或需要与现代浏览器 Intl API 深度集成。它由 Moment 团队开发,采用不可变对象和面向对象设计,适合需要处理复杂时区逻辑的国际化产品,但要求运行环境支持 Intl(不支持 IE11)。
🔥️ NEW: date-fns v4.0 with first-class time zone support is out!
date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in a browser & Node.js
👉 Blog
It's like Lodash for dates
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
See date-fns.org for more details, API, and other docs.