JS踩坑-检查日期是否有效

692 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

前言

本以为检查一个字符串的日期是否有效,应该及其简单,信手拈来的事。然而,事实证明,JS 中那里都有坑,一不留神就深陷坑中无法自拔。

事件起源于一个 excel 文件导入系统功能,里面有一个时间字段用于记录信息的时间,但是因为经手的人很多,造成里面各种信息的格式不一,尤其是时间,那么在导入系统之前,需要把时间的格式统一起来。

然后,发现里面有一些奇奇怪怪的时间,比如 06/3121/2/29 之类,应该是输入笔误的问题,所以还得检测一个时间的是否有效。

处理过程

最初的想法

利用的 Date.parse() 函数,可以接受一个字符串日期作为参数,如果能够返回毫秒数,说明日期是有效的,如果返回 NaN ,说明日期是无效的。

const date1 = Date.parse('2022/6/30')
console.log('date1:', date1) // date1: 1656518400000
const date2 = Date.parse('2022/6/32')
console.log('date2:', date2) // date2: NaN

但是,万万没想到,2022/6/31 居然也被判断为合法的,只是会被表示为 2022/7/1

const date3 = Date.parse('2022/6/31')
console.log('date3:', date3)
// date3: 1656604800000 能够被正常转换为毫秒数
console.log('date3.toLocalString:', (new Date(date3)).toLocaleString())
// date3.toLocalString: 2022/7/1 00:00:00 再次转换为字符串时,变成了七月一号

这显然不对啊,虽然六月三十一号从理论上来说,就是七月一号,但并不符合笔误的可能,7/16/31 区别还是挺大的,所以这个只能是不合法。

找找原因

MDN 上,说是要符合 RFC2822 或 ISO 8601 日期格式的字符串即可。

经过本人测试,在日期上,不论是那个月份,日期取值在 [1 - 31] 之间都是合法的,哪怕是 2/31 都是合法的,可以转换的,可以理解为从 2 月底的开始重新算另一个月,在计算过了多少天,就是多少号:

const date3 = Date.parse('2022/2/31')
console.log('date3:', date3)
// date3: 1646236800000
console.log('date3.toLocalString:', (new Date(date3)).toLocaleString())
// date3.toLocalString: 2022/3/3 00:00:00
// 变成了 3/3

加上字符串对比

那么就加上一道对比,先把日期字符串转换为毫秒数,再把毫秒数转换为字符串日期,再把转换前后的日期进行对比即可:

const isLegalDate = date => date === (new Date(Date.parse(date)).toLocaleDateString())

而且不用担心 Date.parse 转换为 NaN 的问题,因为传入 NaN 之后,(new Date(NaN).toLocaleDateString() 会变成 'invalid Date' 这么一个字符串,不会全等于原来的日期字符串。

其实,这个函数也可以写成如下:

const isLegalDate = date => date === (new Date(date).toLocaleDateString())

直接节省掉 Date.parse 函数,但是MDN并不支持这么做,主要是不同浏览器对不同格式之间的日期字符串的处理有差异。