4
\$\begingroup\$

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";
}
\$\endgroup\$
1
  • \$\begingroup\$ Can't you just use Math.floor once? You're multiplying by whole numbers. \$\endgroup\$ Commented Jan 14, 2017 at 21:29

3 Answers 3

3
\$\begingroup\$

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'
}
\$\endgroup\$
4
  • \$\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
    Commented 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\$ Commented Jan 14, 2017 at 21:27
  • \$\begingroup\$ Ok expanded the answer then. \$\endgroup\$
    – FabioCosta
    Commented 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
    Commented Jan 20, 2017 at 13:08
2
\$\begingroup\$

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)));

\$\endgroup\$
2
  • \$\begingroup\$ How about a simple hashmap for units, like {d : 60 * 60* 24, … }? \$\endgroup\$
    – lxg
    Commented 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
    Commented Jan 15, 2017 at 8:56
2
\$\begingroup\$

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])));

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.