day.js和date-fns怎么比较日期大小

作者:闭包旅人

时间:2026-11-03

浏览:279

全面讲解在 JavaScript 中使用 Day.js 和 date-fns 比较日期大小的具体方法,涵盖基础先后判断、精度截断(按日/月比较)、边界判断以及数组排序的实现细节与机制差异。

文章目录
在 JavaScript 项目中比较两个日期的大小,最主流的两种现代化方案是 Day.js 与 date-fns。Day.js 沿用类似 Moment.js 的面向对象包装模式,通过 isBefore()、isAfter()、isSame() 等实例方法进行比较;而 date-fns 遵循函数式编程思想,直接接收原生 Date 对象,提供 isBefore、isAfter、isEqual 以及用于排序的 compareAsc 等纯函数。本文将从设计机制、常用比较语法、精度截断(按日/月比较)、边界判断以及数组排序五个维度,详细梳理两者的具体用法与实现细节。
day.js和date-fns怎么比较日期大小
图 1:Day.js 与 date-fns 日期比较方式概览

一、核心机制差异:对象包装 vs 原生纯函数

理解两者的比较方式,关键在于理解它们如何管理时间数据。两者的底层设计哲学决定了调用方式与使用习惯的差异:

  • Day.js(面向对象包装):使用 dayjs('2026-05-01') 会生成一个不可变的 Dayjs 包装实例。所有的比较行为均作为该实例的原型方法存在。其优势是支持链式调用,并且支持在比较方法中直接传入粒度单位(例如 'day'、'month')进行自动范围截断。
  • date-fns(函数式无副作用):模块内导出的全部是纯函数。它不需要包装成特殊对象,直接把 JavaScript 原生的 Date 对象作为参数传入(例如 isBefore(dateA, dateB))。其优势是零封装内存开销,天然支持构建工具的 Tree-shaking 摇树优化,按需引入对应函数即可。
day.js和date-fns怎么比较日期大小
图 2:Day.js 包装实例模型与 date-fns 纯函数模型的数据流对比

二、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)。
day.js和date-fns怎么比较日期大小
图 3:日期比较决策与执行判断流程图

三、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 判断日期是否落在特定起止区间内(包含首尾两端)。
day.js和date-fns怎么比较日期大小
图 4:Day.js 与 date-fns 常见比较场景的 API 对照映射

四、日期数组排序与数值比较

当需要对一个包含多个日期的数组进行升序或降序排列时,比较方式直接决定了代码的简洁度与执行性能。

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怎么比较日期大小
图 5:Day.js 与 date-fns 选型考量与最佳实践决策导图

六、对比总结与选型建议

两者在日期大小比较的准确性与功能完整度上都非常成熟,主要区别在于项目架构与编码偏好:

考量维度 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 则是更加契合现代前端工程的选择。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章