day.js和date-fns怎么比较日期大小
全面讲解在 JavaScript 中使用 Day.js 和 date-fns 比较日期大小的具体方法,涵盖基础先后判断、精度截断(按日/月比较)、边界判断以及数组排序的实现细节与机制差异。
isBefore()、isAfter()、isSame() 等实例方法进行比较;而 date-fns 遵循函数式编程思想,直接接收原生 Date 对象,提供 isBefore、isAfter、isEqual 以及用于排序的 compareAsc 等纯函数。本文将从设计机制、常用比较语法、精度截断(按日/月比较)、边界判断以及数组排序五个维度,详细梳理两者的具体用法与实现细节。
一、核心机制差异:对象包装 vs 原生纯函数
理解两者的比较方式,关键在于理解它们如何管理时间数据。两者的底层设计哲学决定了调用方式与使用习惯的差异:
-
Day.js(面向对象包装):使用
dayjs('2026-05-01')会生成一个不可变的 Dayjs 包装实例。所有的比较行为均作为该实例的原型方法存在。其优势是支持链式调用,并且支持在比较方法中直接传入粒度单位(例如'day'、'month')进行自动范围截断。 -
date-fns(函数式无副作用):模块内导出的全部是纯函数。它不需要包装成特殊对象,直接把 JavaScript 原生的
Date对象作为参数传入(例如isBefore(dateA, dateB))。其优势是零封装内存开销,天然支持构建工具的 Tree-shaking 摇树优化,按需引入对应函数即可。
二、Day.js 比较日期大小的实现方法
Day.js 内置了常用的基础比较方法,能满足大部分业务场景;当需要包含边界值(即“小于等于”或“大于等于”)时,需按其插件规范引入对应模块。
1. 基础先后比较与相等判断
Day.js 提供了三个基础实例方法:.isBefore()(早于)、.isAfter()(晚于)和 .isSame()(相等)。这些方法默认精确到毫秒。
import dayjs from 'dayjs';
const d1 = dayjs('2026-05-01 10:00:00');
const d2 = dayjs('2026-05-01 18:00:00');
const d3 = dayjs('2026-05-02 08:00:00');
// 1. 判断 d1 是否早于 d2(结果:true)
console.log(d1.isBefore(d2));
// 2. 判断 d3 是否晚于 d1(结果:true)
console.log(d3.isAfter(d1));
// 3. 判断是否完全相等(默认精确到毫秒,结果:false)
console.log(d1.isSame(d2));
2. 精度控制(按天、月、年比较)
在实际业务(如排班、活动有效期判断)中,我们通常只关心“是否是同一天”或“是否属于同一月份”,而不希望时分秒产生干扰。Day.js 允许在比较方法的第二个参数传入精度单位,例如 'day'、'month'、'year'。
// 传入第二个参数 'day',自动截断到当天 00:00:00 比较
// d1 和 d2 虽然时间不同,但属于同一天(结果:true)
console.log(d1.isSame(d2, 'day'));
// 比较 d1 是否早于 d3(按天比较,2026-05-01 早于 2026-05-02,结果:true)
console.log(d1.isBefore(d3, 'day'));
3. 包含边界的比较:引入 isSameOrBefore / isSameOrAfter 插件
为了保证核心包体积仅约 2KB,Day.js 默认没有将“小于等于”和“大于等于”打包进核心。如果需要进行包含边界的比较,必须通过 dayjs.extend() 加载对应插件:
import dayjs from 'dayjs';
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
dayjs.extend(isSameOrBefore);
dayjs.extend(isSameOrAfter);
const start = dayjs('2026-05-01');
const current = dayjs('2026-05-01');
// 判断 start 是否小于等于 current(结果:true)
console.log(start.isSameOrBefore(current));
// 结合精度单位判断:current 是否大于等于 start 当天(结果:true)
console.log(current.isSameOrAfter(start, 'day'));
dayjs.extend(isSameOrBefore) 就直接使用 d.isSameOrBefore(),运行时会抛出 TypeError: d.isSameOrBefore is not a function 错误。如果不想引入插件,可以使用布尔逻辑组合:d1.isSame(d2) || d1.isBefore(d2)。
三、date-fns 比较日期大小的实现方法
date-fns 的设计思想是“一个功能一个函数”。所有函数均接收原生的 Date 对象(或时间戳、ISO 格式字符串),返回布尔值或数字。
1. 基础先后比较与相等判断
在 date-fns 中,通过按需导入 isBefore、isAfter、isEqual 来完成基础毫秒级比较:
import { isBefore, isAfter, isEqual } from 'date-fns';
const dateA = new Date('2026-05-01T10:00:00');
const dateB = new Date('2026-05-01T18:00:00');
const dateC = new Date('2026-05-02T08:00:00');
// 判断 dateA 是否早于 dateB(结果:true)
console.log(isBefore(dateA, dateB));
// 判断 dateC 是否晚于 dateA(结果:true)
console.log(isAfter(dateC, dateA));
// 判断两者毫秒是否相等(结果:false)
console.log(isEqual(dateA, dateB));
2. 粒度控制:使用专用比较函数
不同于 Day.js 通过传参控制精度,date-fns 为不同粒度的比较提供了独立的命名函数。例如比较是否为同一天使用 isSameDay,比较同一月使用 isSameMonth:
import { isSameDay, isSameMonth, isSameYear } from 'date-fns';
// 虽然时间不同,但均在 2026 年 5 月 1 日(结果:true)
console.log(isSameDay(dateA, dateB));
// 判断是否属于同一个月(结果:true)
console.log(isSameMonth(dateA, dateC));
3. 包含边界的比较实现
date-fns 原生并没有名为 isSameOrBefore 的单一函数。在实际开发中,有以下两种规范做法:
-
逻辑反转法(最简洁):“A 小于等于 B”在数学逻辑上等价于“A 不晚于 B”。因此直接使用
!isAfter(dateA, dateB)即可完成判断。 -
显式条件或区间判断:组合
isBefore(dateA, dateB) || isEqual(dateA, dateB);或者使用isWithinInterval判断日期是否落在特定起止区间内(包含首尾两端)。
四、日期数组排序与数值比较
当需要对一个包含多个日期的数组进行升序或降序排列时,比较方式直接决定了代码的简洁度与执行性能。
1. Day.js 的数组排序写法
Dayjs 对象重写了 valueOf() 方法,调用时会返回对应毫秒级时间戳。因此,在 JavaScript 原生的 Array.prototype.sort 中可以直接利用减法运算:
const dateList = [
'2026-05-03',
'2026-05-01',
'2026-05-02'
];
// 升序排列(从早到晚)
const sortedAsc = [...dateList].sort((a, b) => dayjs(a).valueOf() - dayjs(b).valueOf());
// 简化写法(隐式类型转换会触发 valueOf)
const sortedAscSimple = [...dateList].sort((a, b) => dayjs(a) - dayjs(b));
2. date-fns 的数组排序写法
date-fns 专门内置了 compareAsc(升序)和 compareDesc(降序)辅助函数,其返回值符合 sort 回调规范(早于返回 -1,晚于返回 1,相等返回 0):
import { compareAsc, compareDesc } from 'date-fns';
const dateObjects = [
new Date('2026-05-03'),
new Date('2026-05-01'),
new Date('2026-05-02')
];
// 升序排列:直接作为回调传入,代码高度精简
const sortedAsc = [...dateObjects].sort(compareAsc);
// 降序排列(从晚到早)
const sortedDesc = [...dateObjects].sort(compareDesc);
五、避坑指南:日期比较中的常见问题排查
1. 时分秒引起的隐蔽判断错误
如果日期来源包含不同时间部分(例如前端 DatePicker 选取的 00:00:00 与后端时间戳返回的 15:30:20),直接使用 isBefore 或 isSame 进行按天比对必然出现误判。
- Day.js 解决办法:明确指定比较精度,如
d1.isSame(d2, 'day');或者在比较前通过d1.startOf('day')将时分秒归零。 - date-fns 解决办法:使用
isSameDay(d1, d2);或者使用startOfDay(date)统一规格化后再比较。
2. 动态输入下的无效日期(Invalid Date)
当比较的输入来自于用户表单或外部 API 时,格式错误会导致解析失败。如果直接拿无效日期比较,比较方法可能会返回 false,导致逻辑静默失效:
// Day.js 安全校验
const d = dayjs(userInput);
if (!d.isValid()) {
throw new Error('无效的日期输入');
}
// date-fns 安全校验
import { isValid } from 'date-fns';
if (!isValid(new Date(userInput))) {
throw new Error('无效的日期输入');
}
六、对比总结与选型建议
两者在日期大小比较的准确性与功能完整度上都非常成熟,主要区别在于项目架构与编码偏好:
| 考量维度 | Day.js | date-fns |
|---|---|---|
| API 风格 | 面向对象,支持方法链式调用 | 函数式编程,独立按需引入 |
| 核心优势 | API 与 Moment.js 高度兼容,易读易写,一个方法支持单位参数 | 无封装对象开销,与原生 Date 天然互通,Tree-shaking 彻底 |
| 精度控制方式 | 方法入参统一指定('day', 'month', 'year') | 针对不同粒度使用不同函数(isSameDay, isSameMonth) |
| 包含边界比较 | 需显式注册 isSameOrBefore 等插件 | 推荐逻辑反转 !isAfter() 或组合判断 |
| 数组排序支持 | 利用 .valueOf() 或减法数值运算 |
提供原生 compareAsc / compareDesc 回调 |
在实际技术选型中:如果团队更习惯 d1.isBefore(d2) 这种自然语言般的链式语义,或者工程正从老旧的 Moment.js 进行轻量化重构,Day.js 能提供最平滑的过渡体验;如果项目追求严格的函数式无副作用规范、有严苛的代码体积打包要求,或者大量数据直接以原生 Date 形式流转,date-fns 则是更加契合现代前端工程的选择。
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38








