3

The following code is a countdown timer. It pulls an ending datetime stamp from mySQL and uses it to count to. The issue is that the mysql time may be in a different time zone than the client who is looking at the page with the timer.

I also pull the current timestamp from mySQL with NOW(), thinking that this would allow the timer to count as the user who created it intended.

if I put the NOW() value in this snippet

var timeDiff = target - (new Date()); 

like so

var nt='2015-03-11 05:12:15'.split(/[- :]/);
var timeDiff = target - (new Date(nt[0],nt[1]-1,nt[2],nt[3],nt[4],nt[5]));

the counter shows the correct time left when the page loads but does not count interactively any longer. I think I need to get the difference in hours between the clients local time and the mySQL NOW() and adjust the date in this line to get the interactive timer to run.

var timeDiff = target - (new Date());  

nothing I try seems to work.

This is the working script if the client happens to be int he same time zone.

 <script language="javaScript">              
  document.write(hrs);
  function timeDiff(target) {
    function z(n) {return (n<10? '0' : '') + n;}
    var timeDiff = target - (new Date()); 
    var hours    = timeDiff / 3.6e6 | 0;
    var minutes  = timeDiff % 3.6e6 / 6e4 | 0;
    var seconds  = timeDiff % 6e4 / 1e3 | 0;
    if (hours<0 || minutes<0 || seconds<0) {
      document.getElementById('divBody').style.display='none';
      document.getElementById('divExpired').style.display='';    
      return '<b>EXPIRED</b>';
      }
    else {
      return '<b>' + z(hours) + '</b> Hours, <b>' + z(minutes) + '</b>  Mins, <b>' + z(seconds) + '</b> Secs';
      }
    }
  function doCountDown(target) {
    document.getElementById('timer').innerHTML = '<img src=\"/backhaul/images/my/al-active.png\" class=\"vm2\" /> <span style=\"color:#c40000\"><b>EXPIRES IN</b></span>: ' + timeDiff(target);
    var lag = 1020 - (new Date() % 100);
    setTimeout(function(){doCountDown(target);}, lag);
    }
  window.onload = function() {
    //Insert Expiratin Date from mySQL into t var
    var t='2015-03-12 00:00:00'.split(/[- :]/);
    doCountDown(new Date(t[0],t[1]-1,t[2],t[3],t[4],t[5]));
  }
</script>
8
  • 1
    Please wait for better comments / answers, as I don't have the necessary knowledge. Anyway, if you have to work with timezones, it is better to work with ISO 8601 datetimes from SQL (MySQL has date conversion functions); Javascript could support that format, but in case it doesn't, you can use a javascript library (Google search) Commented Mar 11, 2015 at 22:34
  • 5
    So convert it to UTC.... Commented Mar 11, 2015 at 22:36
  • setInterval() with an AJAX call inside of it may be the way to go for this, then you won't have to deal with Client vs. Server time. The Client time is usually dependent on their computer, which of course they can alter. Commented Mar 11, 2015 at 22:37
  • setInterval is a poor choice for keeping time because the queued events can drift quite a bit, especially if the thread gets busy, a confim() message for example. it would be more reliable to use client time or precision.now() to avoid client-clock adjustments. at any rate, you don't need php, just grab the Date header from any ajax request, subtract the client date, and you're left with the C/S offset in ms. Commented Mar 11, 2015 at 22:44
  • 1
    I think you may find Moment.js very handy for multiple aspects of your code. It's a great JS library for parsing, validating, manipulating, and displaying dates with JavaScript. Commented Mar 23, 2015 at 21:07

2 Answers 2

6
+25

There are many ways of doing this, but I'll elaborate on two ways.

Method 1 : Adjust the time on the client side

One way is what you are trying to do which is to get the current time of the server and find the difference with the client's current time. You can simply adjust the server target time to the client's time. This can be done with

var serverDifference=Date.parse(mysql_data.now)-Date.now()
var target=Date.parse(mysql_data.server_time_in_two_hours)-serverDifference

Then you can input it into your function without problem.

Method 2: Calculate the times remaining, server side

Since you just need a countdown timer, I think it's more appropriate to simply send the seconds left server side. This can be done with SQL using

 select timestampdiff(SECOND,now(),end_time) seconds_left from timers;

Then you simply just make a timer that counts down based on the number of seconds left instead of a target date. You can calculate the number of seconds left by deducting the time that the javascript has run from the time received from the server. So something like

var client_start=Date.now()
function timeDiff_fromseconds(target) {
    function z(n) {return (n<10? '0' : '') + n;}
    var timeDiff =target-(Date.now()-client_start)
    var hours    = timeDiff / 3.6e6 | 0;
    var minutes  = timeDiff % 3.6e6 / 6e4 | 0;
    var seconds  = timeDiff % 6e4 / 1e3 | 0;
    if (hours<0 || minutes<0 || seconds<0) {   
      return '<b>EXPIRED</b>';
      }
    else {
      return '<b>' + z(hours) + '</b> Hours, <b>' + z(minutes) + '</b>  Mins, <b>' + z(seconds) + '</b> Secs';
      }
}

There is also performance.now() as suggested by @dandavis. This returns the number of milliseconds since the tab opened and is accurate to 1/1000th of a millisecond. And this doesn't change even if the system clock of the client browser changes. For full support, you should use a polyfill (As of the time of this writing, iOS Safari doesn't support it). In this context we can replace Date.now() with it.

JSFiddle Demo:

http://jsfiddle.net/3o3u5r5j/1/

Sign up to request clarification or add additional context in comments.

10 Comments

i don't think your drawbacks for #1 are needed. even if the clocks are radically off, both still advance 1 per second, and lag is rarely over 1000ms. there's nothing that says you can't simply calc the # of seconds needed from the first code and then feed that to your 2nd code.
@dandavis Yeah, thinking about it again, you're right. Thanks.
might be worth mentioning that performance.now() is more precise and ignores local clock adjustments made after the tab is opened.
@dandavis Actually the time difference could be massive. I always use a version of Method 2 because you have no idea how badly a users clock could be set. But you can guruntee that 23 seconds ago is the same amount of time on both server and client. Method 2 is the way you should go :)
@MarshallOfSound: interestingly, it doesn't actually matter how far off the client's clock is unless you're showing a clock (and even then arithmetic can adjust). in this case, it's a more-or-less a countdown timer, and the only info you need from the server is the # of seconds in the future from the time of the request. as i mentioned, performance.now() is not tied to the clock, so it can't matter what the client's clock is set to.
|
0

If it is possible to get remaining amount of seconds from database instead of expiry time (meaning to calculate it at the server and send to the client). Then you can use following code (sample). Fiddle

var countDownId;
var timer;
function countDown(){
    console.log(timer--);
    if(timer<=0){
        clearInterval(countDownId);
        alert('time expired');
    }
}

function startCountDown(secondsToExpire){
    var milliseconds = 1 * 1000 ;// 1 second
    timer = secondsToExpire;

    countDownId = setInterval(function() { countDown();},milliseconds);
}

  window.onload = function() {
    //Insert remaining time from expiration
    var timeRemaining = 5;
    startCountDown(timeRemaining);
  }

You can tweak this to suit your needs.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.