使用Javascript将Excel日期序列号转换为日期

Jac*_*ack 12 javascript excel date

我有以下javascript代码将日期(字符串)转换为Microsoft Excel中使用的日期序列号:

function JSDateToExcelDate(inDate) {

    var returnDateTime = 25569.0 + ((inDate.getTime() - (inDate.getTimezoneOffset() * 60 * 1000)) / (1000 * 60 * 60 * 24));
    return returnDateTime.toString().substr(0,5);

}
Run Code Online (Sandbox Code Playgroud)

那么,我该怎么做呢?(这意味着将Microsoft Excel中使用的日期序列号转换为日期字符串的Javascript代码?

sil*_*ire 32

试试这个:

function ExcelDateToJSDate(serial) {
   var utc_days  = Math.floor(serial - 25569);
   var utc_value = utc_days * 86400;                                        
   var date_info = new Date(utc_value * 1000);

   var fractional_day = serial - Math.floor(serial) + 0.0000001;

   var total_seconds = Math.floor(86400 * fractional_day);

   var seconds = total_seconds % 60;

   total_seconds -= seconds;

   var hours = Math.floor(total_seconds / (60 * 60));
   var minutes = Math.floor(total_seconds / 60) % 60;

   return new Date(date_info.getFullYear(), date_info.getMonth(), date_info.getDate(), hours, minutes, seconds);
}
Run Code Online (Sandbox Code Playgroud)

定制为你:)

  • 你可以通过改变时区测试你的解决方案..我在PST时区我不得不减去25568,然后它工作但是在UTC + 10:00它不起作用,请检查utc +和utc-时区. (4认同)
  • 为什么你减去25569即使1970 - 1900 = 25567天?不是说这是第一个在线代码我发现实际上正是因为这个原因. (2认同)
  • 过于复杂的代码。那么时区呢,教授。您是否知道“new Date()”会在您当前的时区中创建日期,而 Excel 序列则采用 UTC。 (2认同)

Wil*_*man 27

简短回答(适用于日期 > 1900-02-28)

new Date(Date.UTC(0, 0, excelSerialDate - 1));
Run Code Online (Sandbox Code Playgroud)

简短答案(适用于日期 <= 1900-02-28)

new Date(Date.UTC(0, 0, excelSerialDate));
Run Code Online (Sandbox Code Playgroud)

为什么这有效

我真的很喜欢@leggett 和@SteveR 的答案,虽然它们大多有效,但我想更深入地了解如何Date.UTC()工作。

注意:时区偏移可能存在问题,特别是对于较旧的日期(1970 年之前)。请参阅浏览器、时区、Chrome 67 错误(历史时区更改),因此我希望保留 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)
Run Code Online (Sandbox Code Playgroud)

JS 中的日期基于Jan 1st 1970 UTC. 如果我们使用Date.UTC(year, month, ?day, ?hour, ?minutes, ?seconds)它将返回自该基准时间(UTC)以来的毫秒数。它有一些有趣的功能,我们可以利用它们来获益。

除了 之外,所有参数的正常范围Date.UTC()都是从 0 开始的day。它确实接受这些范围之外的数字,并将输入转换为超出或下溢其他参数。

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
Run Code Online (Sandbox Code Playgroud)

它也可以处理早于 1970-01-01 的日期。在这里,我们将日期从 0 减到 1,并增加小时、分钟、秒和毫秒。

Date.UTC(1970, 0, 0, 23, 59, 59, 999) is -1ms
Run Code Online (Sandbox Code Playgroud)

它甚至足够智能,可以将 0-99 范围内的年份转换为 1900-1999

Date.UTC(70, 0, 0, 23, 59, 59, 999) is -1ms
Run Code Online (Sandbox Code Playgroud)

现在,我们如何表示 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"
Run Code Online (Sandbox Code Playgroud)

现在我们必须处理时区。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"
Run Code Online (Sandbox Code Playgroud)

我们知道,由于上述闰年问题,该时间会延迟 1 天。我们必须将 day 参数减 1。

new Date(Date.UTC(0, 0, 732 - 1, 0, 0, 0, 0)) gives "1902-01-01T00:00:00.000Z"
Run Code Online (Sandbox Code Playgroud)

需要注意的是,如果我们使用 new Date(year,month,day) 构造函数构造日期,则参数将使用您当地的时区。我在 PT (UTC-7/UTC-8) 时区,我得到

new Date(1902, 0, 1).toISOString() gives me "1902-01-01T08:00:00.000Z"
Run Code Online (Sandbox Code Playgroud)

对于我的单元测试,我使用

new Date(Date.UTC(1902, 0, 1)).toISOString() gives "1902-01-01T00:00:00.000Z"
Run Code Online (Sandbox Code Playgroud)

将 Excel 序列日期转换为 js 日期的 Typescript 函数是

public static SerialDateToJSDate(excelSerialDate: number): Date {
    return new Date(Date.UTC(0, 0, excelSerialDate - 1));
  }
Run Code Online (Sandbox Code Playgroud)

并提取 UTC 日期以供使用

public static SerialDateToISODateString(excelSerialDate: number): string {
   return this.SerialDateToJSDate(excelSerialDate).toISOString().split('T')[0];
 }
Run Code Online (Sandbox Code Playgroud)


Gil*_*Gil 16

我为你做了一个单行班:

function ExcelDateToJSDate(date) {
  return new Date(Math.round((date - 25569)*86400*1000));
}
Run Code Online (Sandbox Code Playgroud)

  • Math.round对我来说不起作用。虽然可以删除它。 (2认同)
  • 这种单缸功能的时间不准确。但是日期是正确的。 (2认同)

leg*_*ett 10

无需做任何数学运算即可将其归结为一行。

// serialDate is whole number of days since Dec 30, 1899
// offsetUTC is -(24 - your timezone offset)
function SerialDateToJSDate(serialDate, offsetUTC) {
  return new Date(Date.UTC(0, 0, serialDate, offsetUTC));
}
Run Code Online (Sandbox Code Playgroud)

我在 PST,它是 UTC-0700,所以我曾经offsetUTC = -17得到 00:00 作为时间(24 - 7 = 17)。

如果您以串行格式从 Google 表格中读取日期,这也很有用。文档建议连续剧可以有一个小数来表示一天的一部分:

指示日期、时间、日期时间和持续时间字段以“序列号”格式输出为双精度值,如 Lotus 1-2-3 所流行的那样。值的整数部分(小数点左边)计算自 1899 年 12 月 30 日以来的天数。小数部分(小数点右边)将时间计算为一天的一小部分。例如,1900 年 1 月 1 日中午是 2.5,2 因为它是 1899 年 12 月 30 日之后的 2 天,0.5 因为中午是半天。1900 年 2 月 1 日下午 3 点将是 33.625。这正确地将 1900 年视为不是闰年。

因此,如果您想支持带小数的序列号,则需要将其分开。

function SerialDateToJSDate(serialDate) {
  var days = Math.floor(serialDate);
  var hours = Math.floor((serialDate % 1) * 24);
  var minutes = Math.floor((((serialDate % 1) * 24) - hours) * 60)
  return new Date(Date.UTC(0, 0, serialDate, hours-17, minutes));
}
Run Code Online (Sandbox Code Playgroud)


Rol*_*man 8

眼镜:

1) https://support.office.com/en-gb/article/date-function-e36c0c8c-4104-49da-ab83-82328b832349

Excel 将日期存储为连续的序列号,以便它们可以用于计算。1900 年 1 月 1 日是序号 1,而 2008 年 1 月 1 日是序号 39448,因为它是 1900 年 1 月 1 日之后的 39,447 天。

2) 还有:https : //support.microsoft.com/en-us/help/214326/excel-incorrectly-assumes-that-the-year-1900-is-a-leap-year

当 Microsoft Multiplan 和 Microsoft Excel 发布时,他们还假设 1900 年是闰年。这种假设允许 Microsoft Multiplan 和 Microsoft Excel 使用与 Lotus 1-2-3 相同的序列日期系统,并提供与 Lotus 1-2-3 的更大兼容性。将 1900 年视为闰年也使用户更容易将工作表从一个程序移动到另一个程序。

3) https://www.ecma-international.org/ecma-262/9.0/index.html#sec-time-values-and-time-range

时间在 ECMAScript 中以 UTC 时间 1970 年 1 月 1 日以来的毫秒为单位进行测量。在时间值中,闰秒被忽略。假设每天正好有 86,400,000 毫秒。

4) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Un​​ix_timestamp

new Date(value)

一个整数值,表示自 1970 年 1 月 1 日 00:00:00 UTC(Unix 纪元)以来的毫秒数,忽略闰秒。请记住,大多数 Unix 时间戳函数只能精确到最接近的秒。

把它放在一起:

function xlSerialToJsDate(xlSerial){
  // milliseconds since 1899-31-12T00:00:00Z, corresponds to xl serial 0.
  var xlSerialOffset = -2209075200000; 

  var elapsedDays;
  // each serial up to 60 corresponds to a valid calendar date.
  // serial 60 is 1900-02-29. This date does not exist on the calendar.
  // we choose to interpret serial 60 (as well as 61) both as 1900-03-01
  // so, if the serial is 61 or over, we have to subtract 1.
  if (xlSerial < 61) {
    elapsedDays = xlSerial;
  }
  else {
    elapsedDays = xlSerial - 1;
  }

  // javascript dates ignore leap seconds
  // each day corresponds to a fixed number of milliseconds:
  // 24 hrs * 60 mins * 60 s * 1000 ms
  var millisPerDay = 86400000;

  var jsTimestamp = xlSerialOffset + elapsedDays * millisPerDay;
  return new Date(jsTimestamp);
}
Run Code Online (Sandbox Code Playgroud)

作为单线:

function xlSerialToJsDate(xlSerial){
  return new Date(-2209075200000 + (xlSerial - (xlSerial < 61 ? 0 : 1)) * 86400000);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

尽管我在讨论开始多年后偶然发现了这个讨论,但我可能对原始问题有一个更简单的解决方案——fwiw,这是我最终将 Excel“自 1899 年 12 月 30 日以来的天数”转换为 JS 日期的方法我需要:

var exdate = 33970; // represents Jan 1, 1993
var e0date = new Date(0); // epoch "zero" date
var offset = e0date.getTimezoneOffset(); // tz offset in min

// calculate Excel xxx days later, with local tz offset
var jsdate = new Date(0, 0, exdate-1, 0, -offset, 0);

jsdate.toJSON() => '1993-01-01T00:00:00.000Z'
Run Code Online (Sandbox Code Playgroud)

本质上,它只是构建一个新的 Date 对象,该对象通过添加 Excel 天数(基于 1),然后通过负本地时区偏移量调整分钟数来计算。


小智 5

我真的很喜欢吉尔的答案,因为它很简单,但它缺少时区偏移。所以,这里是:

function date2ms(d) {
  let date = new Date(Math.round((d - 25569) * 864e5));
  date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
  return date;
}
Run Code Online (Sandbox Code Playgroud)