js new Date(2023,9,13)和new Date('2023-10-13')结果差了整个时区,甚至new Date("2023-1-1")和new Date("2023-01-01")都会差一个时区
如题.
// 9实际指的10月. 因为数值表示的月份取值区间是0~11
new Date(2023,9,12)
//输出 Thu Oct 12 2023 00:00:00 GMT+0800 (中国标准时间)
new Date('2023-10-12')
//输出 Thu Oct 12 2023 08:00:00 GMT+0800 (中国标准时间)
也就是以数值构建date, 结果为中国时间的当天0点, 以字符串构建date,结果为UTC时间的当天0点, 中间差了8小时. 所以在程序中构建date时要注意方式的一致性.
chatGPT3.5不能正确回答, GPT4可以. 如下:
在JavaScript中,new Date(year, month, day)和new Date(dateString)它们创建日期对象的方式是不同的:
new Date(year, month, day):这种方式创建的日期对象,年份、月份、日期都是基于本地时间。JavaScript 数组中月份是从0(一月)开始计数到 11(十二月),所以9其实代表的是10月。
new Date(dateString):如果只提供了一个表示日期的字符串参数,那么该字符串应该按以下格式之一:YYYY-MM-DDTHH:mm:ss.sssZ或者只有日期部分 YYYY-MM-DD, 并且解析为 UTC时间(格林尼治时间)。如果你直接使用new Date('2023-10-12'),那么它会被解析为在UTC时间2023-10-12T00:00:00.000Z,假如你所在的时区比UTC时间早8小时,那么实际上你获得的时间会是2023-10-11T16:00:00.000Z。
由于上述两者不同的工作方式,即使看起来他们似乎在构造相同的日期,但可能得到的Date对象实际上代表的确是两个不同的时间点,表现在getTime()返回的毫秒数上就可能会有差异。
你可以使用 toISOString() 方法来验证这个问题,比如 console.log(new Date(2023, 9, 12).toISOString()) 和 console.log(new Date('2023-10-12').toISOString()),你会看到他们确实是返回了不同的结果。
更加神奇的是, new Date("2023-1-1")和new Date("2023-01-01")都会差一个时区:
new Date('2023-1-1')
> Sun Jan 01 2023 00:00:00 GMT+0800 (中国标准时间)
new Date('2023-01-01')
> Sun Jan 01 2023 08:00:00 GMT+0800 (中国标准时间)
iOS上new Date("2023-1-1")是不能用的, 必须严格输入yyyy-mm-dd
形式.
关于时区可以算是一个Bug, 这篇文章做了详细解释: Web Fundamentals: Avoid These JavaScript Date Object Pitfalls
它列举了这几种构造方式导致时区时有时无:
// ISO 8601 without time: defaults to midnight UTC time
const withHyphen = new Date('2023-01-01') // Sat Dec 31 2022 18:00:00 GMT-0600 (Central Standard Time)
withHyphens.getTime() // 1672531200000
// ISO 8601 set to UTC timezone (same as not providing time)
const noOffset = new Date('2023-01-01T00:00:00.000Z') // Sat Dec 31 2022 18:00:00 GMT-0600 (Central Standard Time)
noOffset.getTime() // 1672531200000
// ISO 8601 set to Local Timezone
const withLocalOffset = new Date('2023-01-01T06:00:00.000Z') // Sun Jan 01 2023 00:00:00 GMT-0600 (Central Standard Time)
withHyphens.getTime() // 1672552800000
// Well Recoginized String Format - Defaults to Local Timezone
const withSlashes = new Date('2023/01/01') // Sun Jan 01 2023 00:00:00 GMT-0600 (Central Standard Time)
withSlashes.getTime() // 1672552800000
// Number Arguments - Defaults to Local Timezone
const withArgs = new Date(2023, 0, 1) // Sun Jan 01 2023 00:00:00 GMT-0600 (Central Standard Time)
withArgs.getTime() // 1672552800000
说原因是 ECMAScript标准在解析ISO 8601日期字符串的时候有瑕疵(flawed), ISO 8601意思是要加上时区, 而ESMAScript没加时区, 发现问题想改的时候已经成为事实标准改不了了. 所以后面其他的实现方式都是按ISO 8601的, 包括/
分隔符和使用数字构建, 或者使用yyyy-m-d方式构建. 最坑爹的是使用yyyy-m-d
方式构建, 当日期从10月9日变到10月10日时, 就会突然失去时区.
另外文中还提到一个问题,就是 new Date(null)是可以构建出正常的Date的,时间戳为0, 也就是1970-1-1, 这就和正常认知有些违背. 不过 new Date(NaN)是不能构建的.