Day.js与date-fns:轻量级JS日期格式化库对比

作者:函数茶馆

时间:2026-11-12

浏览:261

深入对比轻量级JavaScript日期处理库Day.js与date-fns的核心差异,涵盖编程范式、打包体积、Tree-shaking机制、格式化占位符规范、国际化与时区处理,并提供具体的选型决策依据与代码示例。

文章目录
在现代前端与 Node.js 开发中,处理日期的解析、校验、加减运算和展示格式化是普遍需求。鉴于传统 Moment.js 体积庞大且为可变对象设计,官方早已宣布进入维护状态。作为当前最受推崇的两款轻量级替代方案,Day.js 沿袭了链式调用的面向对象 API 并将核心体积控制在约 2KB,而 date-fns 则采用函数式纯函数架构并支持细粒度 Tree-shaking。本文通过剖析两者的编程范式、打包机制、格式化规范及工程落地步骤,为技术团队提供明确的选型与使用指导。
Day.js与date-fns:轻量级JS日期格式化库对比
图 0:Day.js 与 date-fns 核心特性与架构概览缩略图

一、编程范式与 API 设计:链式对象 vs 纯函数

选择日期库时,最先影响开发者编码体验的是 API 的设计哲学。Day.js 和 date-fns 分别代表了面向对象包装器与函数式工具集两种截然不同的范式。

1. Day.js 的链式包装模型

Day.js 保持了与 Moment.js 极高的一致性。通过调用 dayjs(date),它会将传入的日期包裹为一个不可变的 Dayjs 对象实例。所有的日期操作(例如加天数、取月初)都定义在该对象原型链的方法上。调用方法时不会改变原对象,而是返回一个全新的 Dayjs 实例,支持直观的链式连缀:

import dayjs from 'dayjs';

// 链式调用:解析 -> 加 7 天 -> 取当天开始时间 -> 格式化输出
const result = dayjs('2025-05-01')
  .add(7, 'day')
  .startOf('day')
  .format('YYYY-MM-DD HH:mm:ss');

console.log('Day.js 计算结果:', result);
示例输出
Day.js 计算结果: 2025-05-08 00:00:00

这种设计最大的优势是流畅自解释。任何熟悉 Moment.js 或传统面向对象语言的开发者几乎不需要查阅文档就能直接上手,阅读代码时心智负担低。

2. date-fns 的纯函数式工具集

与 Day.js 相反,date-fns 并没有引入任何自定义对象包装,它更像是针对原生 Date 对象的“Lodash”。库中每个功能都是一个独立的纯函数,始终接收原生 JavaScript Date(或时间戳、有效时间字符串)作为输入,计算后返回新的原生 Date 或字符串:

import { addDays, startOfDay, format, parseISO } from 'date-fns';

// 纯函数调用:通过函数嵌套或变量传递组织流水线
const baseDate = parseISO('2025-05-01');
const addedDate = addDays(baseDate, 7);
const startDate = startOfDay(addedDate);
const result = format(startDate, 'yyyy-MM-dd HH:mm:ss');

console.log('date-fns 计算结果:', result);
示例输出
date-fns 计算结果: 2025-05-08 00:00:00

由于 date-fns 始终在操作原生 Date 对象,它能够完美融入函数式管道(如 RxJS、Lodash flow),且与其他仅接收原生 Date 的第三方组件(如各 UI 框架的 DatePicker)天然兼容,不需要额外的 .toDate() 解包动作。

Day.js与date-fns:轻量级JS日期格式化库对比
图 1:Day.js 对象包装流水线与 date-fns 纯函数无状态数据流对比

二、体积与 Tree-shaking:固定基线 vs 按需加载

在关注首屏加载速度的 Web 应用中,JavaScript 包体积是关键考量指标。Day.js 和 date-fns 在打包体积的控制策略上存在根本性差异。

1. Day.js 的全量基线与插件体系

Day.js 将所有常用核心方法(如 format、add、subtract、isBefore 等)打包为一个约 2.0KB ~ 2.5KB (gzip) 的核心包。现代打包工具在引入 dayjs 时,无法将核心中未用到的具体方法剔除(因为所有方法都绑定在同一个类实例上)。

为了保持小体积,Day.js 将相对高阶或非常规功能剥离为独立插件(如 relativeTime、utc、timezone、customParseFormat 等)。如果项目只使用基础日期解析与展示,即使调用了十几个核心方法,整体体积也始终恒定在 2KB 出头;只有显式导入插件时才会轻微增加 0.5KB ~ 1.5KB。

2. date-fns 的细粒度 Tree-shaking 机制

date-fns 彻底基于 ES Module 规范编写。由于每个辅助函数都存放在独立的子模块中,现代打包工具(如 Webpack、Vite、Rollup)能够在构建阶段静态分析代码引用,未被 import 的函数会被 100% 摇树剪裁。

例如,如果一个静态宣传页只需要格式化一个当前年份,仅引入 format 函数的实际增量只有不到 1KB;但值得注意的是,随着项目规模扩大,如果一个复杂企业系统频繁引入了 addDays、subMonths、differenceInDays、eachDayOfInterval 等三四十个函数,date-fns 的打包体积将逐步叠加至 8KB ~ 15KB 以上。

Day.js与date-fns:轻量级JS日期格式化库对比
图 2:两者的构建打包机制解析与函数引入量-体积增长趋势图
体积决策临界点
在通常的前端业务中,如果页面仅涉及 1~5 处简单的展示格式化,date-fns 借助 Tree-shaking 产出的体积最小;但如果项目是复杂的数据中后台系统,涉及大量日期间隔计算、区间判断与多维度转化,Day.js 的固定 2KB 核心反而能提供更稳定的体积边界。

三、格式化规范与常见陷阱:Moment 风格 vs Unicode CLDR

日期格式化中最容易引发线上故障的是格式化占位符(Format Tokens)的定义差异。Day.js 遵循 Moment.js 传统命名,而 date-fns 严格遵循国际标准 Unicode CLDR。

1. 关键占位符差异对照

下表列出了两者最容易混淆的格式化占位符,开发者在编写或重构代码时必须仔细甄别:

目标日期部分 Day.js 占位符 date-fns 占位符 说明与注意事项
四位年份 YYYY yyyy date-fns 中严禁使用 YYYY,否则代表周数年(跨年周易产生偏差)
两位月份 MM MM 两者一致(01 ~ 12)
两位月份中的天 DD dd Day.js 为大写 DD,date-fns 为小写 dd
24 小时制小时 HH HH 两者一致(00 ~ 23)
分钟 mm mm 两者一致(00 ~ 59)
秒数 ss ss 两者一致(00 ~ 59)
一年中的天数 DDD (需插件) DDD 第几天(1 ~ 366)

2. 致命的“周数年”跨年陷阱

在 date-fns 中,如果将常规格式化字符串写成 YYYY-MM-dd,在遇到 12 月 30 日或 31 日恰逢下一公历年的第一周时,YYYY 会直接跳至下一公历年份,引发严重的跨年业务 Bug:

import { format } from 'date-fns';

const targetDate = new Date('2024-12-31T12:00:00Z');

// 错误写法:在 date-fns 中误用 YYYY 代表公历年
const wrongResult = format(targetDate, 'YYYY-MM-dd');
console.log('错误写法结果 (周数年):', wrongResult);

// 正确写法:使用小写 yyyy
const correctResult = format(targetDate, 'yyyy-MM-dd');
console.log('正确写法结果 (公历年):', correctResult);
示例输出
错误写法结果 (周数年): 2025-12-31
正确写法结果 (公历年): 2024-12-31

在 2024 年 12 月 31 日,周数年被判定为 2025 年的第一周,误用 YYYY 导致年份提前跨入 2025 年。因此,在 date-fns 项目中,建议在 ESLint 规则中对 format 调用的 pattern 进行严格审查。

四、落地实践:项目接入与功能实现步骤

将日期库引入前端或 Node.js 项目时,需按照环境初始化、格式校验、语言包加载和时区处理的标准化顺序执行。

Day.js与date-fns:轻量级JS日期格式化库对比
图 3:从依赖接入到功能就绪的工程化实施四步流程

1. 步骤一:安装依赖与模块导入

在项目根目录下通过包管理器安装目标库。两者均拥有完善的社区支持,支持主流 npm/yarn/pnpm:

# 方案 A:安装 Day.js
npm install dayjs

# 方案 B:安装 date-fns
npm install date-fns

2. 步骤二:常用业务计算对比

以下展示日常业务中最常见的“计算距今剩余天数”和“获取当月最后一天”,对比两者的代码组织方式:

// 方式 A:Day.js 实现
import dayjs from 'dayjs';

const now = dayjs();
const deadline = dayjs('2025-12-31');
const daysLeft = deadline.diff(now, 'day');
const endOfMonthStr = now.endOf('month').format('YYYY-MM-DD');

console.log('Day.js 剩余天数:', daysLeft);
console.log('Day.js 月末日期:', endOfMonthStr);
示例输出
Day.js 剩余天数: 234
Day.js 月末日期: 2025-05-31
// 方式 B:date-fns 实现
import { differenceInDays, lastDayOfMonth, format, parseISO } from 'date-fns';

const currentDate = new Date();
const targetDeadline = parseISO('2025-12-31');
const daysRemaining = differenceInDays(targetDeadline, currentDate);
const lastDateStr = format(lastDayOfMonth(currentDate), 'yyyy-MM-dd');

console.log('date-fns 剩余天数:', daysRemaining);
console.log('date-fns 月末日期:', lastDateStr);
示例输出
date-fns 剩余天数: 234
date-fns 月末日期: 2025-05-31

3. 步骤三:中文语言包(i18n)集成

在渲染中文星期、中文月份或相对时间(例如“3天前”)时,需要注入简体中文语言环境:

// Day.js:全局注册模式
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';

dayjs.locale('zh-cn'); // 全局生效,后续所有实例均默认输出中文
console.log('Day.js 中文星期:', dayjs().format('dddd'));
示例输出
Day.js 中文星期: 星期三
// date-fns:局部参数传递模式
import { format } from 'date-fns';
import { zhCN } from 'date-fns/locale';

// 显式将 locale 对象作为选项传入纯函数
const formatted = format(new Date(), 'eeee', { locale: zhCN });
console.log('date-fns 中文星期:', formatted);
示例输出
date-fns 中文星期: 星期三

4. 步骤四:时区(Timezone)与 UTC 处理

处理国际化跨时区业务时,Day.js 需要显式加载 utc 和 timezone 官方插件;date-fns v3/v4 则通过官方维护的 @date-fns/tz 或辅助函数提供时区上下文支持:

// Day.js 时区配置示例
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

const nyTime = dayjs().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss');
console.log('纽约当前时间:', nyTime);
示例输出
纽约当前时间: 2025-05-10 13:30:00

五、总结与选型决策指南

综合上述技术剖析,Day.js 与 date-fns 均为经过海量生产验证的优秀开源库。选择的核心在于团队的技术栈风格与项目的业务特征:

Day.js与date-fns:轻量级JS日期格式化库对比
图 4:Day.js 与 date-fns 全维度技术决策矩阵

选型建议清单

  • 优先选用 Day.js 的场景: 存量项目正在使用 Moment.js 并希望以最低改造成本完成减负迁移;以表单输入、报表统计为主的企业级中后台系统;开发团队更习惯链式连缀语法,希望避免嵌套多层函数调用。
  • 优先选用 date-fns 的场景: 极度追求按需打包与首屏加载速度的移动端 H5 或轻量级 Web 产品;重度采用函数式编程、React Hooks、RxJS 等现代响应式流水线的项目;需要极高 TypeScript 类型推导精度、拒绝任何原型或状态包装对象的工程。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章