携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
前言
本以为检查一个字符串的日期是否有效,应该及其简单,信手拈来的事。然而,事实证明,JS 中那里都有坑,一不留神就深陷坑中无法自拔。
事件起源于一个 excel 文件导入系统功能,里面有一个时间字段用于记录信息的时间,但是因为经手的人很多,造成里面各种信息的格式不一,尤其是时间,那么在导入系统之前,需要把时间的格式统一起来。
然后,发现里面有一些奇奇怪怪的时间,比如 06/31、21/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/1 和 6/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并不支持这么做,主要是不同浏览器对不同格式之间的日期字符串的处理有差异。