- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
在 JavaScript 里判断两个Date对象是否相等并不是一个直观的操作——即使两个对象在概念上(甚至打印出来)完全相同,直接使用===比较也往往得到false。本篇技术指南以 TIL 仓库中的 compare-the-equality-of-two-date-objects.md 为主体,讲解为什么对象比较会"失灵"、如何借助getTime()把日期转换为数字来完成可靠判断,以及<、>、<=、>=运算符为何可以直接用于日期比较,并延伸介绍与之配套的日期解析、时区与毫秒/秒陷阱。
为什么两个"一样"的 Date 对象不相等
JavaScript 中的相等性判断对"对象"有一套与基本类型完全不同的规则。对于基本类型(数字、字符串等),===比较的是值;而对于对象(包括Date实例),===比较的是引用——即两个变量是否指向内存中的同一个对象。
所以,即使两个Date对象内部携带完全相同的时刻,只要它们是通过两次独立构造产生的,就会被视为"不相等"。这是理解日期相等性比较的第一块基石:
const now = Date.now(); const today1 = new Date(now); const today2 = new Date(now); today1 === today2 // false —— 两个独立对象,引用不同用 getTime() 将日期转换为数字再比较
要判断两个Date对象是否代表同一时刻,正确思路是先把它们转换成一个可以用值比较的东西——数字。Date.prototype.getTime()正是为此而生的:它返回该日期相对于 Unix 纪元(1970-01-01 00:00:00 UTC)的毫秒数,是一个纯数字,天然适合相等性判断。
> now = Date.now() > today1 = new Date(now) > today2 = new Date(now) > today1 === today2 false > today1.getTime() === today2.getTime() true由于getTime()返回的是 UTC 毫秒时间戳,这种比较方式天然不受本地时区影响:只要两个日期对象指向同一时刻,无论当前机器时区如何,它们的getTime()结果都完全一致。
其他等价写法
除了显式调用getTime(),下面几种写法也能得到同样的"数字化"效果,可在不同场景下选用:
// 一元加号运算符,等价于调用 valueOf() +date1 === +date2 // 显式调用 valueOf() date1.valueOf() === date2.valueOf() // 对象转数字的隐式触发 Number(date1) === Number(date2)Date对象的valueOf()与getTime()语义相同,都返回毫秒时间戳。需要提醒的是,这类写法在代码可读性上不如getTime()直白,团队协作时优先推荐显式写法。
比较大小:Date 对象可以直接使用 <、>、<=、>=
与相等性判断不同,Date对象可以直接使用关系运算符<、>、<=、>=来比较先后顺序:
const earlier = new Date('2024-01-01T00:00:00Z'); const later = new Date('2024-06-01T00:00:00Z'); later > earlier // true earlier < later // true earlier <= later // true later >= earlier // true原因在于:关系运算符在遇到对象时,会先通过内部的ToPrimitive转换把对象强制转换为原始值(对Date而言优先得到数字毫秒时间戳),然后再按数字大小进行比较。因此later > earlier实际等价于later.getTime() > earlier.getTime(),比较的是时间戳数值,结果可靠且符合直觉。
区分两种情况:相等用 getTime(),大小直接用运算符
把两种场景放在一起对比,结论非常清晰:
| 比较目标 | 推荐写法 | 说明 |
|---|---|---|
| 是否相等(同一时刻) | a.getTime() === b.getTime() | ===比较引用,必须先转为数字 |
| 是否相等(宽松) | a == b(不推荐) | 依赖隐式转换,可读性差,容易误用 |
| 先后/大小 | a < b、a > b、a <= b、a >= b | 运算符自动完成 ToPrimitive 转换,可直接使用 |
务必警惕a == b这类宽松相等写法:虽然Date对象在==下会触发隐式转换从而"碰巧"能工作,但这种写法语义晦涩,也容易在与其他类型的值混用时产生意外结果,实际项目中应避免。
封装一个可复用的日期相等性函数
为了把"先转数字再比较"的细节隔离在单个入口,可以封装一个小工具函数,方便在项目各处复用:
function isSameDate(a, b) { if (!(a instanceof Date) || !(b instanceof Date)) { throw new TypeError('isSameDate expects Date instances'); } return a.getTime() === b.getTime(); } isSameDate(new Date('2024-01-01T00:00:00Z'), new Date('2024-01-01T00:00:00Z')) // trueinstanceof Date的类型检查可以避免传入null、字符串等非日期值导致难以排查的NaN === NaN(恒为false)问题。
延伸:日期比较前必须绕开的三个陷阱
围绕"比较两个日期"这一主题,TIL 仓库中还记录了若干直接影响比较结果的关键细节,比较前务必核对:
1. 字符串日期可能被按 UTC 解释(ISO-8601 陷阱)
用字符串构造Date时,解析规则并不统一。仓库中的 iso-8601-formatted-dates-are-interpreted-as-utc.md 记录了这样一个事实:非 ISO-8601 格式(如'2017-12-4')按本地时区解释,而符合 ISO-8601 的格式(如'2017-12-04')按UTC解释,二者可能相差数小时、甚至"跨天"。因此在用字符串构造日期后再做比较时,要明确输入格式的时区语义。
2. 秒与毫秒:传入整数时差了一千倍
仓库中的 parse-a-date-from-a-timestamp.md 和 convert-seconds-to-date-object.md 都强调:new Date(1618499080)期望的是毫秒时间戳,而很多后端 API 返回的是秒。直接传入秒级时间戳会得到一个接近 1970 年的怪异日期,比较结果自然完全错误。正确做法是乘以1000:
new Date(1713350171 * 1000) // 2024-04-17T10:36:11.000Z相应地,Date.now()和getTime()返回的也都是毫秒,注意保持单位一致。
3. 时间分量与日期运算的辅助函数
仓库中的 get-the-time-components-of-a-date.md 整理了getHours()、getMinutes()、getSeconds()、getMilliseconds()、getTimezoneOffset()等时间分量 API;create-future-and-past-dates-from-today.md 则演示了用setDate()/setFullYear()做日期加减运算。这些 API 常用于在比较前后对日期做归一化处理,例如把时间部分清零后再判断"是否同一天"。
替代方案:用 DayJS 等库简化比较
如果项目已经引入日期库,可以借助其封装好的比较方法省去手动转换。仓库中的 easy-date-comparison-with-dayjs.md 记录了用 DayJS 判断日期是否已过期的做法:
import dayjs from 'dayjs'; const today = dayjs(new Date()); const pastDate = dayjs("2018-10-22"); const futureDate = dayjs("2022-01-01"); console.log(pastDate.isBefore(today)); // => true console.log(futureDate.isBefore(today)); // => falsedayjs()可以从Date对象或字符串构造 DayJS 实例,再用isBefore()、isAfter()等函数直接比较。不过对于"两个日期是否相等"这种简单需求,原生getTime()已经足够,是否引入依赖可按项目实际情况权衡。
小结
- 两个独立构造的
Date对象永远不能用===判断相等,因为对象比较的是引用; - 判断"是否代表同一时刻"请用
a.getTime() === b.getTime(); - 判断"谁先谁后"可以直接使用
<、>、<=、>=,运算符会自动把日期转换为数值时间戳; - 比较之前注意字符串解析的时区规则、秒/毫秒单位差异等陷阱,必要时参考 get-the-time-components-of-a-date.md、iso-8601-formatted-dates-are-interpreted-as-utc.md 与 convert-seconds-to-date-object.md 等笔记。
更多相关实践可继续阅读 TIL 仓库的 JavaScript 目录 下的日期主题笔记,例如 basic-date-formatting-without-a-library.md(用Intl.DateTimeFormat做格式化)与 easy-date-comparison-with-dayjs.md(DayJS 比较)。
- 文档
- 教程
- 知识库
【免费下载链接】til
:memo: Today I Learned
相关推荐
30 Seconds of Code:JavaScript 中两个 Date 对象的比较(相等、先后与区间判断)完整指南
30 Seconds of Code:JavaScript 中两个 Date 对象的比较(相等、先后与区间判断)完整指南 JavaScript 的 Date 对
教程文档Mengzi-T5-Base性能评测:在8大中文NLP任务中的表现分析
Mengzi T5 Base性能评测:在8大中文NLP任务中的表现分析 Mengzi T5 Base是一款针对中文优化的T5系列预训练模型,在文本生成、翻译、摘
Bun深度比较:对象相等性判断的精准方法
Bun深度比较:对象相等性判断的精准方法 引言:为什么需要深度比较? 在日常JavaScript开发中,对象比较是一个常见但容易出错的操作。传统的 === 操作
语言运行时后端开发工具包管理器前端构建测试
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考