22

I'm new to JavaScript and I'm trying to write a code which calculates the time elapsed from the time a user logged in to the current time.

Here is my code:-

function markPresent() {
    window.markDate = new Date();
    $(document).ready(function() {
        $("div.absent").toggleClass("present");
    });
    updateClock();
}

function updateClock() {    
    var markMinutes = markDate.getMinutes();
    var markSeconds = markDate.getSeconds();

    var currDate = new Date();
    var currMinutes = currDate.getMinutes();
    var currSeconds = currDate.getSeconds();
    var minutes = currMinutes - markMinutes;
    if(minutes < 0) { minutes += 60; }
    var seconds = currSeconds - markSeconds;
    if(seconds < 0) { seconds += 60; }

    if(minutes < 10) { minutes = "0" + minutes; }
    if(seconds < 10) { seconds = "0" + seconds; }

    var hours = 0;
    if(minutes == 59 && seconds == 59) { hours++; }
    if(hours < 10) { hours = "0" + hours; }

    var timeElapsed = hours+':'+minutes+':'+seconds;
    document.getElementById("timer").innerHTML = timeElapsed;
    setTimeout(function() {updateClock()}, 1000);
}

The output is correct upto 00:59:59 but after that that O/P is:

00:59:59 01:59:59 01:59:00 01:59:01 . . . . 01:59:59 01:00:00

How can I solve this and is there a more efficient way I can do this? Thank you.

Gaurav Mahawar
  • 412
  • 3
  • 5
  • 14
  • 3
    Get the unixtimestamp and then calculate it from that. (Date.now()) – NEO Jul 14 '15 at 12:02
  • 3
    `if(minutes == 59 && seconds == 59) { hours; }` doesn't do anything. – Magicprog.fr Jul 14 '15 at 12:02
  • I would suggest looking at a lib such as moment.js, it has a lot of utility functions for calculating durations, etc: http://momentjs.com/ – Ted Nyberg Jul 14 '15 at 12:07
  • by the way, its bad practice to work with global objects aka: "window.markDate" – Ori Price Jul 14 '15 at 12:14
  • There is no need for all that (or converting anything), because `Date` objects are directly comparable to each other! _E.g._, when I need to get double-click events (for simplicity, regardless of where the user clicked), I set a global variable `LastClickTime=new Date()`, then set an `onclick` function to get `CurrentTime=new Date()`, which checks if `CurrentTime-LastClickTime – Cyberknight Oct 26 '18 at 18:29

9 Answers9

30

No offence, but this is massively over-enginered. Simply store the start time when the script first runs, then subtract that from the current time every time your timer fires.

There are plenty of tutorials on converting ms into a readable timestamp, so that doesn't need to be covered here.

    var start = Date.now();
    
    setInterval(function() {
      document.getElementById('difference').innerHTML = Date.now() - start;
    
      // the difference will be in ms
    }, 1000);
<div id="difference"></div>
LWC
  • 1,084
  • 1
  • 10
  • 28
Christian
  • 19,605
  • 3
  • 54
  • 70
7

There's too much going on here.

An easier way would just be to compare markDate to the current date each time and reformat.

See Demo: http://jsfiddle.net/7e4psrzu/

function markPresent() {
    window.markDate = new Date();
    $(document).ready(function() {
        $("div.absent").toggleClass("present");
    });
    updateClock();
}

function updateClock() {  
    var currDate = new Date();
    var diff = currDate - markDate;
    document.getElementById("timer").innerHTML = format(diff/1000);
    setTimeout(function() {updateClock()}, 1000);
}

function format(seconds)
{
var numhours = parseInt(Math.floor(((seconds % 31536000) % 86400) / 3600),10);
var numminutes = parseInt(Math.floor((((seconds % 31536000) % 86400) % 3600) / 60),10);
var numseconds = parseInt((((seconds % 31536000) % 86400) % 3600) % 60,10);
    return ((numhours<10) ? "0" + numhours : numhours)
    + ":" + ((numminutes<10) ? "0" + numminutes : numminutes)
    + ":" + ((numseconds<10) ? "0" + numseconds : numseconds);
}

markPresent();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="timer"></div>
LWC
  • 1,084
  • 1
  • 10
  • 28
Curtis
  • 101,612
  • 66
  • 270
  • 352
7

Here is a solution I just made for my use case. I find it is quite readable. The basic premise is to simply subtract the timestamp from the current timestamp, and then divide it by the correct units:

const showElapsedTime = (timestamp) => {
    if (typeof timestamp !== 'number') return 'NaN'        

    const SECOND = 1000
    const MINUTE = 1000 * 60
    const HOUR = 1000 * 60 * 60
    const DAY = 1000 * 60 * 60 * 24
    const MONTH = 1000 * 60 * 60 * 24 * 30
    const YEAR = 1000 * 60 * 60 * 24 * 30 * 12
    
    // const elapsed = ((new Date()).valueOf() - timestamp)
    const elapsed = 1541309742360 - timestamp
    
    if (elapsed <= MINUTE) return `${Math.round(elapsed / SECOND)}s`
    if (elapsed <= HOUR) return `${Math.round(elapsed / MINUTE)}m`
    if (elapsed <= DAY) return `${Math.round(elapsed / HOUR)}h`
    if (elapsed <= MONTH) return `${Math.round(elapsed / DAY)}d`
    if (elapsed <= YEAR) return `${Math.round(elapsed / MONTH)}mo`
    return `${Math.round(elapsed / YEAR)}y`
}
      
const createdAt = 1541301301000

console.log(showElapsedTime(createdAt + 5000000))
console.log(showElapsedTime(createdAt))
console.log(showElapsedTime(createdAt - 500000000))

For example, if 3000 milliseconds elapsed, then 3000 is greater than SECONDS (1000) but less than MINUTES (60,000), so this function will divide 3000 by 1000 and return 3s for 3 seconds elapsed.

If you need timestamps in seconds instead of milliseconds, change all instances of 1000 to 1 (which effectively multiplies everything by 1000 to go from milliseconds to seconds (ie: because 1000ms per 1s).

Here are the scaling units in more DRY form:

const SECOND = 1000
const MINUTE = SECOND * 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const MONTH = DAY * 30
const YEAR = MONTH * 12
agm1984
  • 15,500
  • 6
  • 89
  • 113
7

We can also use console.time() and console.timeEnd() method for the same thing.

Syntax:

console.time(label);
console.timeEnd(label);

Label: The name to give the new timer. This will identify the timer; use the same name when calling console.timeEnd() to stop the timer and get the time output to the console.

let promise = new Promise((resolve, reject) => setTimeout(resolve, 400, 'resolved'));

// Start Timer
console.time('x');

promise.then((result) => {
  console.log(result);

  // End Timer
  console.timeEnd('x');
});
Varun Sukheja
  • 6,170
  • 5
  • 51
  • 93
6

You can simply use performance.now()

Example:

start = performance.now();

elapsedTime = performance.now() - start;
shaedrich
  • 5,457
  • 3
  • 26
  • 42
GeoW
  • 61
  • 1
  • 3
0
var hours = 0;
if(minutes == 59 && seconds == 59) 
{ 
    hours = hours + 1; 
    minutes = '00'; 
    seconds == '00';
}
Kram
  • 526
  • 5
  • 11
  • This fixes one problem but not the other (between 1:59:59 and 2:00:00) because the hours variable is reset to zero every time. – JJJ Jul 14 '15 at 12:08
  • Bad habit to convert numbers to strings. You'll end up with problems in other languages, but Javascript won't really care. – MortenMoulder Jul 14 '15 at 12:11
  • 2
    Also, `seconds == '00'` doesn't do anything. – JJJ Jul 14 '15 at 12:15
0

I would use the getTime() method, subtract the time and then convert the result into hh:mm:ss.mmm format.

cs04iz1
  • 1,737
  • 1
  • 17
  • 30
0

I know this is kindda old question but I'd like to apport my own solution in case anyone would like to have a JS encapsulated plugin for this. Ideally I would have: start, pause, resume, stop, reset methods. Giving the following code all of the mentioned can easily be added.

(function(w){
  
  var timeStart,
      timeEnd,
      started = false,
      startTimer = function (){
        this.timeStart = new Date();
        this.started = true;
      },
      getPartial = function (end) {
        if (!this.started)
          return 0;
        else {
          if (end) this.started = false;
          this.timeEnd = new Date();
          return (this.timeEnd - this.timeStart) / 1000;
        }
      },
      stopTime = function () {
        if (!this.started)
          return 0;
        else {
          return this.getPartial(true);
        }
      },
      restartTimer = function(){
        this.timeStart = new Date(); 
      };
  w.Timer = {
    start : startTimer,
    getPartial : getPartial,
    stopTime : stopTime,
    restart : restartTimer
  };
})(this);
<a href="#" onclick="Timer.start()">Start</a>
<a href="#" onclick="Timer.getPartial()">Partial</a>
<a href="#" onclick="Timer.stopTime()">Stop</a>
<a href="#" onclick="Timer.restart()">Restart</a>
dibiler
  • 11
  • 2
0

What I found useful is a 'port' of a C++ construct (albeit often in C++ I left show implicitly called by destructor):

var trace = console.log
function elapsed(op) {
    this.op = op
    this.t0 = Date.now()
}
elapsed.prototype.show = function() {
    trace.apply(null, [this.op, 'msec', Date.now() - this.t0, ':'].concat(Array.from(arguments)))
}

to be used - for instance:

function debug_counters() {
    const e = new elapsed('debug_counters')
    const to_show = visibleProducts().length
    e.show('to_show', to_show)
}
CapelliC
  • 59,646
  • 5
  • 47
  • 90