Преобразование Excel Date Serial Number в Date с помощью Javascript
У меня есть следующий код 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);
}
Итак, как мне сделать обратное? (Значит, что код Javascript, который преобразует Серийный номер даты, используемый в Microsoft Excel, в строку даты?
Ответы
Ответ 1
Попробуйте следующее:
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);
}
Пользовательский для вас:)
Ответ 2
Я сделал для вас один-лайнер:
function ExcelDateToJSDate(date) {
return new Date(Math.round((date - 25569)*86400*1000));
}
Ответ 3
Хотя я наткнулся на эту дискуссию спустя годы после ее начала, у меня может быть более простое решение первоначального вопроса - fwiw, вот как я в итоге сделал преобразование из Excel "дней с 1899-12-30" в JS Date Мне было нужно:
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), а затем корректировки минут по отрицательному смещению локального часового пояса.
Ответ 4
Нет необходимости делать какие-либо математические операции, чтобы свести их к одной строке.
// 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 Sheets в серийном формате. Документация предполагает, что у серийного номера может быть десятичная дробь, чтобы выразить часть дня:
Указывает, что поля даты, времени, даты и времени должны быть выведены в виде двойных чисел в формате "серийный номер", как это популярно в Lotus 1-2-3. Целая числовая часть значения (слева от десятичной дроби) считает дни с 30 декабря 1899 года . Дробная часть (справа от десятичной дроби) считает время как часть дня. Например, 1 января 1900 года в полдень будет 2,5, 2, потому что это 2 дня после 30 декабря 1899 года, и 0,5, потому что полдень - это полдня. 1 февраля 1900 года в 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));
}
Ответ 5
Таким образом, у меня возникла та же проблема, затем возникли некоторые решения, но у меня начались проблемы с локалью, часовыми поясами и т.д., Но в итоге я смог добавить необходимую точность
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)
}
Аргумент функции time выбирает отображение всей даты или только даты и времени.
Ответ 6
Технические характеристики:
1) https://support.office.com/en-gb/article/date-function-e36c0c8c-4104-49da-ab83-82328b832349
Excel сохраняет даты в виде последовательных серийных номеров, чтобы их можно было использовать в расчетах. 1 января 1900 года - серийный номер 1, а 1 января 2008 года - серийный номер 39448, поскольку после 1 января 1900 года это 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 в миллисекундах с 1 января 1970 года по Гринвичу. В значениях времени високосные секунды игнорируются. Предполагается, что есть ровно 86 400 000 миллисекунд в день.
4) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Unix_timestamp
new Date(value)
Целочисленное значение, представляющее количество миллисекунд с 1 января 1970 года, 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);
}
Как однострочник:
function xlSerialToJsDate(xlSerial){
return new Date(-2209075200000 + (xlSerial - (xlSerial < 61 ? 0 : 1)) * 86400000);
}
Ответ 7
// Parses an Excel Date ("serial") into a
// corresponding javascript Date in UTC+0 timezone.
//
// Doesn't account for leap seconds.
// Therefore is not 100% correct.
// But will do, I guess, since we're
// not doing rocket science here.
//
// https://www.pcworld.com/article/3063622/software/mastering-excel-date-time-serial-numbers-networkdays-datevalue-and-more.html
// "If you need to calculate dates in your spreadsheets,
// Excel uses its own unique system, which it calls Serial Numbers".
//
lib.parseExcelDate = function (excelSerialDate) {
// "Excel serial date" is just
// the count of days since `01/01/1900`
// (seems that it may be even fractional).
//
// The count of days elapsed
// since `01/01/1900` (Excel epoch)
// till `01/01/1970` (Unix epoch).
// Accounts for leap years
// (19 of them, yielding 19 extra days).
const daysBeforeUnixEpoch = 70 * 365 + 19;
// An hour, approximately, because a minute
// may be longer than 60 seconds, see "leap seconds".
const hour = 60 * 60 * 1000;
// "In the 1900 system, the serial number 1 represents January 1, 1900, 12:00:00 a.m.
// while the number 0 represents the fictitious date January 0, 1900".
// These extra 12 hours are a hack to make things
// a little bit less weird when rendering parsed dates.
// E.g. if a date `Jan 1st, 2017` gets parsed as
// `Jan 1st, 2017, 00:00 UTC` then when displayed in the US
// it would show up as `Dec 31st, 2016, 19:00 UTC-05` (Austin, Texas).
// That would be weird for a website user.
// Therefore this extra 12-hour padding is added
// to compensate for the most weird cases like this
// (doesn't solve all of them, but most of them).
// And if you ask what about -12/+12 border then
// the answer is people there are already accustomed
// to the weird time behaviour when their neighbours
// may have completely different date than they do.
//
// `Math.round()` rounds all time fractions
// smaller than a millisecond (e.g. nanoseconds)
// but it unlikely that an Excel serial date
// is gonna contain even seconds.
//
return new Date(Math.round((excelSerialDate - daysBeforeUnixEpoch) * 24 * hour) + 12 * hour);
};