我有以下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);
}
那么,我该怎么办呢? (这意味着将Microsoft Excel中使用的日期序列号转换为日期字符串的Javascript代码?
答案 0 :(得分:31)
试试这个:
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);
}
为您定制:)
答案 1 :(得分:14)
我为你做了一个单行:
function ExcelDateToJSDate(date) {
return new Date(Math.round((date - 25569)*86400*1000));
}
答案 2 :(得分:1)
无需进行任何数学运算即可将结果降至一行。
// 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));
}
我在PST(UTC-0700)上,所以我用offsetUTC = -17
来获得00:00的时间(24-7 = 17)。
如果您要以串行格式从Google表格中读取日期,这也很有用。 The documentation建议序列号可以使用小数来表示一天的一部分:
指示日期,时间,日期时间和持续时间字段以“序列号”格式的双精度形式输出,这在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年视为as年。
因此,如果要支持带小数的序列号,则需要将其分开。
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));
}
答案 3 :(得分:1)
我真的很喜欢Gil的答案,因为它很简单,但是缺少时区偏移。因此,这里是:
function date2ms(d) {
let date = new Date(Math.round((d - 25569) * 864e5));
date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
return date;
}
答案 4 :(得分:0)
尽管我在开始讨论的几年后偶然发现了这个问题,但对于原始问题,我可能有一个更简单的解决方案-顺便说一句,这是我最终从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'
从本质上讲,它只是构建一个新的Date对象,该对象通过添加Excel天数(从1开始),然后通过负本地时区偏移量来调整分钟来计算。
答案 5 :(得分:0)
所以,我遇到了同样的问题,然后出现了一些解决方案,但是开始遇到了区域设置,时区等问题,但最终却能够增加所需的精度
toDate(serialDate, time = false) {
let locale = navigator.language;
let offset = new Date(0).getTimezoneOffset();
let date = new Date(0, 0, serialDate, 0, -offset, 0);
if (time) {
return serialDate.toLocaleTimeString(locale)
}
return serialDate.toLocaleDateString(locale)
}
函数的“时间”参数在显示整个日期还是仅显示日期时间之间进行选择
答案 6 :(得分:0)
规格:
1)https://support.office.com/en-gb/article/date-function-e36c0c8c-4104-49da-ab83-82328b832349
Excel将日期存储为序列号,以便可以 用于计算。 1900年1月1日是序列号1,1月 1月1日是2008年1月,因为它是1月之后的39447天 1900年1月1日。
当Microsoft Multiplan和Microsoft Excel发布时,它们也 假设1900年是a年。这个假设使Microsoft Multiplan和Microsoft Excel使用相同的序列日期系统 由Lotus 1-2-3提供,并提供与Lotus 1-2-3的更大兼容性。 将1900年视为a年,也使用户更容易移动 从一个程序到另一个程序的工作表。
3)https://www.ecma-international.org/ecma-262/9.0/index.html#sec-time-values-and-time-range
时间是自1970年1月1日以来以ECMAScript为单位的毫秒数。 世界标准时间。在时间值上,leap秒将被忽略。假设那里 每天精确地为86,400,000毫秒。
4)https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Unix_timestamp
new Date(value)
一个整数值,表示自此以来的毫秒数 1970年1月1日00:00:00 UTC(Unix时代),带有leap秒 忽略了。请记住,大多数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);
}
单线:
function xlSerialToJsDate(xlSerial){
return new Date(-2209075200000 + (xlSerial - (xlSerial < 61 ? 0 : 1)) * 86400000);
}
答案 7 :(得分:0)
这是一个旧线程,但是希望我可以为您节省编写npm软件包所需的时间:
$ npm install
js-excel-date-convert
const toExcelDate = require('js-excel-date-convert').toExcelDate;
const fromExcelDate = require('js-excel-date-convert').fromExcelDate;
const jul = new Date('jul 5 1998');
toExcelDate(jul); // 35981 (1900 date system)
fromExcelDate(35981); // "Sun, 05 Jul 1998 00:00:00 GMT"
您可以使用https://docs.microsoft.com/en-us/office/troubleshoot/excel/1900-and-1904-date-system
中的示例验证这些结果function fromExcelDate (excelDate, date1904) {
const daysIn4Years = 1461;
const daysIn70years = Math.round(25567.5 + 1); // +1 because of the leap-year bug
const daysFrom1900 = excelDate + (date1904 ? daysIn4Years + 1 : 0);
const daysFrom1970 = daysFrom1900 - daysIn70years;
const secondsFrom1970 = daysFrom1970 * (3600 * 24);
const utc = new Date(secondsFrom1970 * 1000);
return !isNaN(utc) ? utc : null;
}
function toExcelDate (date, date1904) {
if (isNaN(date)) return null;
const daysIn4Years = 1461;
const daysIn70years = Math.round(25567.5 + 1); // +1 because of the leap-year bug
const daysFrom1970 = date.getTime() / 1000 / 3600 / 24;
const daysFrom1900 = daysFrom1970 + daysIn70years;
const daysFrom1904Jan2nd = daysFrom1900 - daysIn4Years - 1;
return Math.round(date1904 ? daysFrom1904Jan2nd : daysFrom1900);
}
如果您想了解其工作原理,请检查:https://bettersolutions.com/excel/dates-times/1904-date-system.htm
答案 8 :(得分:0)
我真的很喜欢 @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 为单位。它有一些有趣的功能,我们可以从中受益。
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
它也可以处理早于 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];
}
答案 9 :(得分:-1)
Default (*)
答案 10 :(得分:-1)
感谢@silkfire 的解决方案!
经过我的验证。我发现当你在东半球时,@silkfire 有正确的答案;西半球则相反。
因此,要处理时区,请参见下文:
function ExcelDateToJSDate(serial) {
// Deal with time zone
var step = new Date().getTimezoneOffset() <= 0 ? 25567 + 2 : 25567 + 1;
var utc_days = Math.floor(serial - step);
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);
}