javascript I want to do moment subtract or diff - javascript

I want to subtract the time between event.startDate and nextEvent.startDate.
hello.json
{
"event": {
"id": 932,
"title": "123",
"startDate": "2023-01-17T00:40:00.000Z",
"endDate": "2023-01-17T03:29:46.000Z"
},
"nextEvent": {
"id": 930,
"title": "minsuhi",
"startDate": "2023-01-18T13:20:00.000Z",
"endDate": "2023-01-18T16:09:46.000Z"
}
}
I tried subtraction using diff and tried console.log, but a strange value of 1970-01-02 21:01:00 is output.
If you subtract the two values ​​from each other, the year should come out as 2023 - 2023 = 0000, but 1970 comes out and inquires.
Isn't that what subtracting with diff is? How should I do the calculation?
export default function View() {
const startTime = moment(hello.data?.event?.startDate);
const nextStartTime = moment(hello.data?.nextEvent?.startDate);
const duration = nextStartTime.diff(startTime)
const formattedDuration = moment(duration).format('YYYY-MM-DD HH:MM:SS')
console.log(formattedDuration) // 1970-01-02 21:01:00
return (
)
}

moment.diff() gives you the difference between two dates, in milliseconds. The following code snippet:
const startTime = moment(hello.data?.event?.startDate);
const nextStartTime = moment(hello.data?.nextEvent?.startDate);
const duration = nextStartTime.diff(startTime)
Gives you the difference in time in milliseconds, but by now converting it to a moment object, you are telling it the equivalent of "duration number of milliseconds after January 1st, 1970" (this date is the default that most computers use as a "time zero").
If you want to get individual differences in time, you'd have to specify individually:
// The false here specifies whether or not you want an integer (false)
// or a floating point number (true)
const durationYears = nextStartTime.diff(startTime, "years", false)
const durationMonths = nextStartTime.diff(startTime, "months", false) % 12
const durationDays = nextStartTime.diff(startTime, "days", false) % 365
const durationHours = nextStartTime.diff(startTime, "hours", false)
const durationMinutes = nextStartTime.diff(startTime, "minutes", false) % 60
const durationSeconds = nextStartTime.diff(startTime, "seconds", false) % 60
By having these modulos at the end, you force it to stay under it's allotted time. For example, 63 minutes mod 60 is just 3, which is what we want to keep when displaying our numbers. For any time, consider what it carries into, and at what mark (i.e. seconds carry into minutes at 60, months carry into years at 12, etc.). By running the modulo operator on each number, we guarantee it will be less than that number.

You might want to reference the documentation for MomentJS Duration
Getting a duration from moment.diff() should be like this:
const duration = moment.duration(nextStartTime.diff(startTime))
Also, I'm not quite sure why you are trying to format a duration as a date since it is a length of period, not an exact date.
You could use these to present a duration:
console.log(duration.humanize()); // Prints '2 days' (rounded)
console.log(duration.toISOString()); // Prints 'P1DT12H40M' which is ISO-8601 representation of a duration/period that is 1 day, 12 hours and 40 minutes.
For a full working example:
const startTime = moment("2023-01-17T00:40:00.000Z");
const nextStartTime = moment("2023-01-18T13:20:00.000Z");
const duration = moment.duration(nextStartTime.diff(startTime))
console.log(duration.humanize());
console.log(duration.toISOString());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.8.1/moment.min.js"></script>

Related

How can I determine if multiples of a duration fit evenly between two dates?

I'm using Luxon, and have two dates and an ISO duration. How can I determine if the duration between the two dates is an multiple of the duration with no remainder?
1/23/2000 to 6/23/2000 with P6M returns true
1/23/2000 to 1/23/2022 with P6M returns true
1/23/2000 to 2/23/2000 with P6M returns false
1/23/2000 to 2/23/2022 with P1M returns true
1/23/2000 to 2/24/2022 with P1M returns false
I considered converting to "months" for example and just using Javascript's modulus operator to determine if the remainder is 0, but ISO durations can be very complex, eg "P3Y6M4DT12H30M5S" represents a duration of "three years, six months, four days, twelve hours, thirty minutes, and five seconds".
Intervals have a splitBy function that seems promising as it leaves the remainder as the last element in the array it returns, but I'm not sure how to then compare that element to my original duration:
import {DateTime, Duration, Interval} from 'luxon';
const date1: DateTime = DateTime.fromJSDate(new Date('12/20/2022'));
const date2: DateTime = DateTime.fromJSDate(new Date('12/20/2023'));
const duration: Duration = Duration.fromISO('P6M');
const interval: Interval = Interval.fromDateTimes(date1, date2);
const split = interval.splitBy(duration);
const modulusDuration = (split[split.length - 1] as Interval).toDuration();
if (modulusDuration.valueOf() === duration.valueOf()) {
console.log('pass'); // We never get here
} else {
// We get here
console.log(modulusDuration.rescale().toObject()) // {months: 6, days: 3, hours: 1}
console.log(duration.rescale().toObject()) // {months: 6}
console.log('fail');
}
Here's a stackblitz: https://stackblitz.com/edit/typescript-kuwjpg?file=index.ts
How can I determine if multiples of a duration fit neatly between two dates?
Thanks to #RobG's comment I was able to get this working by iteratively adding the duration to the start date and checking if it lands exactly on the end date:
import {DateTime, Duration, Interval} from 'luxon';
const date1: DateTime = DateTime.fromJSDate(new Date('1/20/2022'));
const date2: DateTime = DateTime.fromJSDate(new Date('1/20/2023'));
const duration: Duration = Duration.fromISO('P6M');
let pass = false;
for (let i = date1; i <= date2; i = i.plus(duration)) {
if (i.toMillis() === date2.toMillis()) {
pass = true;
}
}
console.log(pass ? 'pass' : 'fail');
https://stackblitz.com/edit/typescript-nodwnx?file=index.ts

Difference between two dates. react-native (js)

I am trying to get the time that has happened between two dates.
Example:
oldDate = "4/16/2020, 12:00:00"
today = "5/17/2021, 1:00:50"
Result that a need:
years = 1
months = 1
days = 1
hours = 1
minutes = 0
seconds = 50
I also would like to refresh it every second, so that, the user can see it running.
Thanks.
Use Date.parse() to get a Date object from a string, subtract two dates using the - operator, and get the number of years, months, days, etc. using the corresponding methods. Then use setInterval to run it every second, and Date.now() to get the current time.
const oldDate = Date.parse("4/10/2020, 12:00:00");
// weird but seems to work with all dates
const getYear = (date) => date.getFullYear() - 1970;
const timeBetweenDates = (date1, date2) => {
const date = new Date(date1 - date2);
return {
years: getYear(date),
months: date.getMonth(),
days: date.getDay(),
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds(),
}
}
const pre = document.getElementById("display");
setInterval(() => {
pre.textContent = JSON.stringify(
timeBetweenDates(Date.now(), oldDate),
0,
2
);
}, 1000);
<pre id="display">Wait a second...</pre>

How To Get The Sum of Two Times Using Moment.js?

I want to add two times like time1 = '00:05' and time2 = '10:00'. I want the result like the following after sum: result='10:05'. I used moment for that, this is what I used:
let x = moment.duration(moment(this.time1, "hh:mm A").add(moment(this.time2, "hh:mm A")));
let result = moment.utc(x.asMilliseconds()).format('HH:mm:ss');
but I got nothing, how can I do it?
You can't add time this way with moment because you are asking it to add two times, not a time plus a duration. If you want to add ten minutes, use the add() function with a duration.
moment(this.time2, "hh:mm A").add(10, 'minutes')
More here: https://momentjs.com/docs/#/manipulating/add/
It's not really clear in your question what 00:05 PM means. That doesn't look like a valid time. Moment will interpret it as 12:05pm, but it looks like you want to interpret it as 5 minutes. (That's the only way you get 10:05 as an answer). You can do this with moment if you don't include the PM part of the string.
moment.duration('00:05')
Is a duration of five minutes. You can add this to your time with:
moment('10:00 PM', '"hh:mm A"').add(moment.duration('00:05'))
// 22:05:00
Adding two periods works but it is currently not obvious in moment, how to format it like you want. Until they add format() to durations this will have to do:
var d = moment.duration('03:10:10').add(moment.duration('01:20:30'))
moment.utc(d.as('milliseconds')).format("HH:mm:ss")
// '04:30:40'
See Example For Add & Diff using moment.js .. cheers😀
// addTimes(["01:00", "00:30", "00:50"]) ---- 02:20
addTimes(times) {
let duration = 0;
times.forEach(time => {
duration = duration + moment.duration(time).as('milliseconds')
});
return moment.utc(duration).format("HH:mm")
}
// subtractTimes(["05:00", "00:30", "00:20"]) --- 04:10
subtractTimes(times) {
let totalDiff = 0
for (let i = 0; i < times.length; i++) {
let duration = moment.duration(times[i]).as('milliseconds')
if (i == 0) {
totalDiff = duration
}
if (i > 0) {
totalDiff = totalDiff - duration
}
}
return moment.utc(totalDiff).format("HH:mm")
}
function addTwoHours(firstTime = "20:40", secondTime = "18:40") {
firstTime = firstTime.split(':');
secondTime = secondTime.split(':');
const now = moment();
const expiration = moment().add({ hour: firstTime[0], minute: firstTime[1] }).add({ hour: secondTime[0], minute: secondTime[1] });
const diff = expiration.diff(now);
const diffDuration = moment.duration(diff);
return {
"years": diffDuration.years(),
"months": diffDuration.months(),
"days": diffDuration.days(),
"hours": diffDuration.hours(),
"minutes": diffDuration.minutes(),
"yourAnswer" : `${expiration.diff(now, 'hours')}:${diffDuration.minutes()}`
}
}
console.log(addTwoHours());
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.17.1/moment.min.js"></script>

Converting human string time between different units of time

Are you aware of a npm module capable of converting human string time to days, hours, seconds or milliseconds preferably using moment.js time units?
It's a little difficult to explain, so here are a few examples:
'1hours' to minutes = 60
'2days' to seconds = 172800
'60seconds' to minutes = 1
'30minutes' to seconds = 1800
Same as above using short hand:
'1h' to minutes = 60
'2d' to seconds = 172800
'60s' to minutes = 1
'30m' to seconds = 1800
These are the string units used by moment.js
Key Shorthand
----------------------
years y
quarters Q
months M
weeks w
days d
hours h
minutes m
seconds s
milliseconds ms
Or expressed as a function:
const convertUnits = (input, format) => {
// Implementation
};
convertUnits('1hours', 'minutes') // 60
convertUnits('1h', 'm') // 60
Or is it possible to do this just using moment? Remember that I don't care about the actual date or time - I merely want the unit conversions mixed in with the human readable units.
Thanks in advance.
I ended up using below:
import * as _ from 'lodash'
import moment from 'moment'
import momentDurationFormat from 'moment-duration-format'
export const convertTime = (time, format = 's') => {
if(!_.isString(time) || !_.isString(format)){
return 0;
}
const components = time.trim().split(/(\d+)/);
const digits = parseInt(components[1]);
const unit = components[2];
return moment
.duration(digits, unit)
.format(format);
};
Try to use timestring library. It parse a human readable time string into a time based value (by default in seconds). As I see, it can all you need:
const timestring = require('timestring')
let str = '1d 3h 25m 18s'
let time = timestring(str)
console.log(time) // will log 98718
str input param can be set without spaces, for example, '1d3h25m18s'

Converting Excel Date Serial Number to Date using Javascript

I have the following javascript code that convert date (string) to the Date Serial Number used in 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);
}
So, how do I do the reverse? (Meaning that a Javascript code that convert the Date Serial Number used in Microsoft Excel to a date string?
Try this:
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);
}
Custom made for you :)
I made a one-liner for you:
function ExcelDateToJSDate(date) {
return new Date(Math.round((date - 25569)*86400*1000));
}
The Short Answer
new Date(Date.UTC(0, 0, excelSerialDate - 1));
Why This Works
I really liked the answers by #leggett and #SteveR, and while they mostly work, I wanted to dig a bit deeper to understand how Date.UTC() worked.
Note: There could be issues with timezone offsets, especially for older dates (pre-1970). See Browsers, time zones, Chrome 67 Error (historic timezone changes) so I'd like to stay in UTC and not rely on any shifting of hours if at all possible.
Excel dates are integers based on Jan 1st, 1900 (on PC. on MAC it is based from Jan 1st, 1904). Let's assume we are on a 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)
Dates in JS are based on Jan 1st 1970 UTC. If we use Date.UTC(year, month, ?day, ?hour, ?minutes, ?seconds) it will return the number of milliseconds since that base time, in UTC. It has some interesting functionality which we can use to our benefit.
All normal ranges of the parameters of Date.UTC() are 0 based except day. It does accept numbers outside those ranges and converts the input to over or underflow the other parameters.
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
It can do dates earlier than 1970-01-01 too. Here, we decrement the day from 0 to 1, and increase the hours, minutes, seconds and milliseconds.
Date.UTC(1970, 0, 0, 23, 59, 59, 999) is -1ms
It's even smart enough to convert years in the range 0-99 to 1900-1999
Date.UTC(70, 0, 0, 23, 59, 59, 999) is -1ms
Now, how do we represent 1900-01-01? To easier view the output in terms of a date I like to do
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"
Now we have to deal with timezones. Excel doesn't have a concept of a timezone in its date representation, but JS does. The easiest way to work this out, IMHO, is to consider all Excel dates entered as UTC (if you can).
Start with an Excel date of 732.0
new Date(Date.UTC(0, 0, 732, 0, 0, 0, 0)).toISOString() gives "1902-01-02T00:00:00.000Z"
which we know is off by 1 day because of the leap year issue mentioned above. We must decrement the day parameter by 1.
new Date(Date.UTC(0, 0, 732 - 1, 0, 0, 0, 0)) gives "1902-01-01T00:00:00.000Z"
It is important to note that if we construct a date using the new Date(year, month, day) constructor, the parameters use your local timezone. I am in the PT (UTC-7/UTC-8) timezone and I get
new Date(1902, 0, 1).toISOString() gives me "1902-01-01T08:00:00.000Z"
For my unit tests, I use
new Date(Date.UTC(1902, 0, 1)).toISOString() gives "1902-01-01T00:00:00.000Z"
A Typescript function to convert an excel serial date to a js date is
public static SerialDateToJSDate(excelSerialDate: number): Date {
return new Date(Date.UTC(0, 0, excelSerialDate - 1));
}
And to extract the UTC date to use
public static SerialDateToISODateString(excelSerialDate: number): string {
return this.SerialDateToJSDate(excelSerialDate).toISOString().split('T')[0];
}
Specs:
1) https://support.office.com/en-gb/article/date-function-e36c0c8c-4104-49da-ab83-82328b832349
Excel stores dates as sequential serial numbers so that they can be
used in calculations. January 1, 1900 is serial number 1, and January
1, 2008 is serial number 39448 because it is 39,447 days after January
1, 1900.
2) But also: https://support.microsoft.com/en-us/help/214326/excel-incorrectly-assumes-that-the-year-1900-is-a-leap-year
When Microsoft Multiplan and Microsoft Excel were released, they also
assumed that 1900 was a leap year. This assumption allowed Microsoft
Multiplan and Microsoft Excel to use the same serial date system used
by Lotus 1-2-3 and provide greater compatibility with Lotus 1-2-3.
Treating 1900 as a leap year also made it easier for users to move
worksheets from one program to the other.
3) https://www.ecma-international.org/ecma-262/9.0/index.html#sec-time-values-and-time-range
Time is measured in ECMAScript in milliseconds since 01 January, 1970
UTC. In time values leap seconds are ignored. It is assumed that there
are exactly 86,400,000 milliseconds per day.
4) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Unix_timestamp
new Date(value)
An integer value representing the number of milliseconds since
January 1, 1970, 00:00:00 UTC (the Unix epoch), with leap seconds
ignored. Keep in mind that most Unix Timestamp functions are only
accurate to the nearest second.
Putting it together:
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);
}
As one-liner:
function xlSerialToJsDate(xlSerial){
return new Date(-2209075200000 + (xlSerial - (xlSerial < 61 ? 0 : 1)) * 86400000);
}
No need to do any math to get it down to one line.
// 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));
}
I'm in PST which is UTC-0700 so I used offsetUTC = -17 to get 00:00 as the time (24 - 7 = 17).
This is also useful if you are reading dates out of Google Sheets in serial format. The documentation suggests that the serial can have a decimal to express part of a day:
Instructs date, time, datetime, and duration fields to be output as doubles in "serial number" format, as popularized by Lotus 1-2-3. The whole number portion of the value (left of the decimal) counts the days since December 30th 1899. The fractional portion (right of the decimal) counts the time as a fraction of the day. For example, January 1st 1900 at noon would be 2.5, 2 because it's 2 days after December 30st 1899, and .5 because noon is half a day. February 1st 1900 at 3pm would be 33.625. This correctly treats the year 1900 as not a leap year.
So, if you want to support a serial number with a decimal, you'd need to separate it out.
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));
}
I really liked Gil's answer for it's simplicity, but it lacked the timezone offset. So, here it is:
function date2ms(d) {
let date = new Date(Math.round((d - 25569) * 864e5));
date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
return date;
}
Although I stumbled onto this discussion years after it began, I may have a simpler solution to the original question -- fwiw, here is the way I ended up doing the conversion from Excel "days since 1899-12-30" to the JS Date I needed:
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'
Essentially, it just builds a new Date object that is calculated by adding the # of Excel days (1-based), and then adjusting the minutes by the negative local timezone offset.
So, there I was, having the same problem, then some solutions bumped up but started to have troubles with the Locale, Time Zones, etc, but in the end was able to add the precision needed
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)
}
The function's 'time' argument chooses between displaying the entire date or just the date's time
Thanks for #silkfire's solution!
After my verification. I found that when you're in the Eastern Hemisphere, #silkfire has the right answer; The western hemisphere is the opposite.
So, to deal with the time zone, see below:
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);
}
// 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's unlikely that an Excel serial date
// is gonna contain even seconds.
//
return new Date(Math.round((excelSerialDate - daysBeforeUnixEpoch) * 24 * hour) + 12 * hour);
};
dart implementation of #silkfire answer
DateTime getDateFromSerialDay(double serial) {
final utc_days = (serial - 25569).floor();
final utc_value = utc_days * 86400;
final date_info = DateTime.fromMillisecondsSinceEpoch(utc_value * 1000);
final fractional_day = serial - utc_days + 0.0000001;
var total_seconds = (86400 * fractional_day).floor();
var seconds = total_seconds % 60;
total_seconds -= seconds;
var hours = (total_seconds / (60 * 60) % 24).floor();
var minutes = ((total_seconds / 60) % 60).floor();
return DateTime(date_info.year, date_info.month, date_info.day, hours,
minutes, seconds);
}
It's an old thread but hopefully I can save you the time I used readying around to write this npm package:
$ npm install js-excel-date-convert
Package Usage:
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"
You can verify these results with the example at https://learn.microsoft.com/en-us/office/troubleshoot/excel/1900-and-1904-date-system
The Code:
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);
}
If you want to know how this works check: https://bettersolutions.com/excel/dates-times/1904-date-system.htm

Categories