Date的解析规则,new Date("1899-12-31")与new Date("1899/12/31")的不同

553 阅读4分钟

前言

笔者负责的项目,后端会返回oaDate作为时间,前端对oaDate的解析规则如下

const OFFSET_TIEM = "1899-12-30" ;

function parseUshort(oaDate:number){
  return new Date(ushort*24*60*60*1000+new Date(OFFSET_TIEM).getTime())
}

但是在某个项目里,发现对于这个OFFSET_TIEM是1899/12/31,导致了一个bug,把44926(这个值应该是2022-12-31T00:00:00.000Z)这个值在英国客户那里解析成了2023年,而我们国内客户解析成2022年。首先这里正确的值应该是1899-12-30,这里讨论下为什么1899/12/31会导致国内客户是2022,而英国客户是2023年,我们的时区可是+8哎,凭什么不是国内是2023呢。

首先说下 new Date解析规则

有许多方法可以将日期格式化为字符串。JavaScript 规范仅指定了一种普遍支持的格式:日期时间字符串格式,这是 ISO 8601 日历日期扩展格式的简化。格式如下:
YYYY-MM-DDTHH:mm:ss.sssZ

  • YYYY是年份,带有四位数字 ( 0000到9999 ),或作为_扩展年份_或后跟六位数字。扩展年份需要该标志。 明确不允许作为有效年份。-000000被明确禁止作为有效年份
  • MM是月份,带有两位数 ( 01到 12)。默认值为01 。
  • DD是月份中的某天,带有两位数 ( 01到31)。默认值为 01。
  • T是一个文本字符,指示字符串_的时间部分的_开始。指定时间部分时需要 T。
  • HH是小时,带有两位数 ( 00到23 )。作为特殊情况,24:00:00是被允许的,并被解释为第二天开始时的午夜。默认值为 00。
  • mm是分钟,带有两位数 ( 00到59 )。默认值为00 。
  • ss是第二个,带有两位数 (00 到 59)。默认值为 00。
  • sss是毫秒,带有三位数字 ( 000到 999)。默认值为 000。
  • Z是时区偏移量,它可以是文字字符Z(指示 UTC),也可以是后跟+HH:mm ,以小时和分钟为单位与 UTC 的偏移量。Z+-HH:mm

可以省略各种组件,因此以下内容都有效:

  • 仅日期形式:YYYY、YYYY-MM、YYYY-MM-DD
  • 日期时间形式:上述仅日期形式之一,后跟 T。每个组合后可以跟一个时区偏移量。THH:mm、HH:mm:ss、HH:mm:ss.sss

例如,仅日期形式(2011-10-10)、日期时间形式(2011-10-10T14:48:00)或具有毫秒和时区(2011-10-10T14:48:00.000+09:00)的日期_时间_形式都是有效的日期时间字符串。
当时区偏移量不存在时,**仅日期形式被解释为 UTC 时间,日期时间形式被解释为本地时间。**这是由于历史规范错误与 ISO 8601 不一致,但由于 Web 兼容性而无法更改。请参阅Broken Parser – A Web Reality Issue
非标准字符串可以根据实现所需的任何方式进行解析,包括时区 — 默认情况下,大多数实现使用本地时区。
建议您确保输入符合上述日期时间字符串格式,以实现最大兼容性,因为无法保证支持其他格式。但是,所有主要实现都支持某些格式(如 RFC 2822 格式),在这种情况下,它们的使用是可以接受的。始终执行跨浏览器测试,以确保您的代码在所有目标浏览器中都能正常工作。如果要容纳许多不同的格式,图书馆可以提供帮助。

错误原因

  • 首先可以确认的是,如果基准值是1899-12-31的话,那英国才是正确的,这里说下为什么
  • 1899/12/31不是一个标准时间格式,那么就会被解释为本地时间,本地时间的话,英国的时区为0时区,得到的时间是刚刚好是2023-01-01T00:00:00.000Z,而国内是+8时区,按本地时间算出来的时间是2022-12-31T15:54:17.000Z,最后format的时候,又是以本地时区进行format的,就出现这个奇怪的问题了

总结

在输入时间的时候,一定要写规范明确支持的格式,比如时间戳或者YYYY-MM-DDTHH:mm:ss.sssZ ,不要想当然的去写,不然你就会遇到各种奇奇怪怪的问题

参考文档

developer.mozilla.org/en-US/docs/…