I use the following code in Javascript to get the difference between two Date objects. I want the result to return the difference in:
- seconds if the result is less than 60 secs
- minutes if the result is less than 60 mins
- hours if the result is less than 24 hours
- days otherwise
The code is very long and I see lots of code duplication. Isn't there a smarter/shorter way to do this (without using a library)?
function dateDiff(a, b) {
let utc1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate(), a.getUTCHours(), a.getUTCMinutes(), a.getUTCSeconds());
let utc2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate(), b.getUTCHours(), b.getUTCMinutes(), b.getUTCSeconds());
let result = (utc2 - utc1) / (1000 * 60 * 60 * 24);
let floor = Math.floor(result);
if (floor > 0) return floor + "d";
result *= 24;
floor = Math.floor(result);
if (floor > 0) return floor + "h";
result *= 60;
floor = Math.floor(result);
if (floor > 0) return floor + "min";
result *= 60;
floor = Math.floor(result);
if (floor > 0) return floor + "sec";
}
3 Answers 3
Use getTime instead of the UTC transformation it'll return the timeStamp.
So your diff would be:
b.getTime() - a.getTime()
This will give you the milliseconds if you want the seconds you would divide it by 1000 so you would get:
var secondsDiff = (b.getTIme() - a.getTime())/1000
Then for the return
if (secondsDiff > 86400) {
return Math.floor(secondsDiff/86400) + ' D'
}
if (secondsDiff > 3600) {
return Math.floor(secondsDiff/3600) + ' h'
}
if (secondsDiff > 60) {
return Math.floor(secondsDiff/60) + ' min'
}
if (secondsDiff > 0) {
return secondsDiff + ' sec'
}
-
\$\begingroup\$ Well it's a broad field of make the code more efficient, but from the start the first thing that catchs the eye is the Utc misuse when you could have getTime. Plus I put the link but there isn't really much to say about getTime \$\endgroup\$FabioCosta– FabioCosta2017年01月14日 21:25:33 +00:00Commented Jan 14, 2017 at 21:25
-
\$\begingroup\$ This is kind of the gray zone. Yes, it's an answer, but I feel that it can be improved and you've been downvoted likely because it's not detailed enough. So if it can be improved, why not improve it? Up to you. \$\endgroup\$David Archibald– David Archibald2017年01月14日 21:27:53 +00:00Commented Jan 14, 2017 at 21:27
-
\$\begingroup\$ Ok expanded the answer then. \$\endgroup\$FabioCosta– FabioCosta2017年01月14日 21:36:19 +00:00Commented Jan 14, 2017 at 21:36
-
\$\begingroup\$ If you combined this with trincot's answer (using a for loop) your code would become even shorter and I'd love to accept it as the right answer codereview.stackexchange.com/a/152683/128754 \$\endgroup\$Timo Ernst– Timo Ernst2017年01月20日 13:08:22 +00:00Commented Jan 20, 2017 at 13:08
You could put it in a loop if you store the specifics about the time units in an array. This is ES6 code:
function dateDiff(a, b) {
const units = [{size: 60*60*24, name: 'd' },
{size: 60*60, name: 'h' },
{size: 60, name: 'min'},
{size: 1, name: 'sec'}];
const result = (b.getTime() - a.getTime()) / 1000;
const unit = units.find( unit => result >= unit.size );
return Math.floor(result / unit.size) + unit.name;
}
console.log(dateDiff(new Date(2016, 0, 14, 20), new Date(2016, 0, 14, 23, 58, 20)));
-
\$\begingroup\$ How about a simple hashmap for
units
, like{d : 60 * 60* 24, ... }
? \$\endgroup\$lxg– lxg2017年01月14日 21:50:38 +00:00Commented Jan 14, 2017 at 21:50 -
\$\begingroup\$ @lxg, sure, but then you have to specify
d
,h
,min
, ... twice, since you need to make sure you iterate in the correct order. \$\endgroup\$trincot– trincot2017年01月15日 08:56:45 +00:00Commented Jan 15, 2017 at 8:56
I wanted to play with the fact that 3 out of 4 if
tests are comparing to exponent of 60 (0, 60 and 3600) and came up with the code that is maybe a line shorter but much less readble. However, was interested if it's faster. I compared it on jsperf.com with FabioCosta's solution as was cutious. https://jsperf.com/datediff-compare2
Performance test result are maybe not enough to reach a conclusion, but out of 10 tests it turns out to be from -2% to +14% faster on Chrome55/Linux.
function dateDiff2(d1,d2) {
var a = (d1.getTime() - d2.getTime())/1000;
if (a >= 86400) return Math.floor(a/86400) + ' D';
var labels=["sec","min","h"];
var p = Math.floor((Math.log(a) / Math.log(60)));
return Math.floor(a/Math.pow(60,p)) + labels[p];
}
/* test data */
var dates=[
[new Date(1483225200000),new Date(1483225199000)],
[new Date(1483225200000),new Date(1483225139000)],
[new Date(1483225200000),new Date(1483221599000)],
[new Date(1483225200000),new Date(1483138799000)]
]
console.log(dates.map(test=>dateDiff2(test[0],test[1])));
Math.floor
once? You're multiplying by whole numbers. \$\endgroup\$