简短的回答
new Date(Date.UTC(0, 0, excelSerialDate - 1));
为什么会这样
我真的很喜欢@leggett 和@SteveR 的答案,虽然它们大部分都有效,但我想更深入地了解Date.UTC() 的工作原理。
注意:时区偏移可能存在问题,尤其是对于较早的日期(1970 年之前)。请参阅Browsers, time zones, Chrome 67 Error (historic timezone changes),所以我想留在 UTC,尽可能不依赖任何时间变化。
Excel 日期是基于 1900 年 1 月 1 日的整数(在 PC 上。在 MAC 上它基于 1904 年 1 月 1 日)。假设我们在 PC 上。
1900-01-01 is 1.0
1901-01-01 is 367.0, +366 days (Excel incorrectly treats 1900 as a leap year)
1902-01-01 is 732.0, +365 days (as expected)
JS 中的日期基于Jan 1st 1970 UTC。如果我们使用Date.UTC(year, month, ?day, ?hour, ?minutes, ?seconds),它将返回自基准时间以来的毫秒数,以UTC 表示。它有一些有趣的功能,我们可以利用这些功能为我们带来好处。
除day外,Date.UTC()的所有参数的正常范围都是从0开始的。它确实接受这些范围之外的数字,并将输入转换为溢出或下溢其他参数。
Date.UTC(1970, 0, 1, 0, 0, 0, 0) is 0ms
Date.UTC(1970, 0, 1, 0, 0, 0, 1) is 1ms
Date.UTC(1970, 0, 1, 0, 0, 1, 0) is 1000ms
它也可以做早于 1970-01-01 的日期。在这里,我们将天从 0 递减到 1,并增加小时、分钟、秒和毫秒。
Date.UTC(1970, 0, 0, 23, 59, 59, 999) is -1ms
它甚至足够聪明,可以将 0-99 范围内的年份转换为 1900-1999
Date.UTC(70, 0, 0, 23, 59, 59, 999) is -1ms
现在,我们如何表示 1900-01-01?为了更容易根据我喜欢的日期查看输出
new Date(Date.UTC(1970, 0, 1, 0, 0, 0, 0)).toISOString() gives "1970-01-01T00:00:00.000Z"
new Date(Date.UTC(0, 0, 1, 0, 0, 0, 0)).toISOString() gives "1900-01-01T00:00:00.000Z"
现在我们必须处理时区。 Excel 在其日期表示中没有时区的概念,但 JS 有。恕我直言,最简单的解决方法是将所有 Excel 日期输入为 UTC(如果可以的话)。
从 Excel 日期 732.0 开始
new Date(Date.UTC(0, 0, 732, 0, 0, 0, 0)).toISOString() gives "1902-01-02T00:00:00.000Z"
由于上面提到的闰年问题,我们知道它会提前 1 天。我们必须将 day 参数减 1。
new Date(Date.UTC(0, 0, 732 - 1, 0, 0, 0, 0)) gives "1902-01-01T00:00:00.000Z"
需要注意的是,如果我们使用 new Date(year, month, day) 构造函数构造日期,则参数使用您当地的时区。我在 PT (UTC-7/UTC-8) 时区,我得到了
new Date(1902, 0, 1).toISOString() gives me "1902-01-01T08:00:00.000Z"
对于我的单元测试,我使用
new Date(Date.UTC(1902, 0, 1)).toISOString() gives "1902-01-01T00:00:00.000Z"
一个将excel序列日期转换为js日期的Typescript函数是
public static SerialDateToJSDate(excelSerialDate: number): Date {
return new Date(Date.UTC(0, 0, excelSerialDate - 1));
}
并提取要使用的 UTC 日期
public static SerialDateToISODateString(excelSerialDate: number): string {
return this.SerialDateToJSDate(excelSerialDate).toISOString().split('T')[0];
}