phpjqueryajax

Static countdown timer based on server time


A client wants a launch page which will show a static countdown and when the countdown is finished the user will be redirected to another page.

The launch is like an event for all users, so I want to make it hard for the user to access the redirected page before the countdown reaches zero - therefore I think it is best to base it on server time instead of the users/clients time, so that the user cant tinker with the time.

The countdown should also reach zero at approximately the same time across (two) different time zones.

I've found a few different scripts and approaches to this across the web, but I can't seem to get it to work.

My jQuery code right now is like this

var end = new Date(Date.UTC(2020,12-1,10,12,00,00)) ;
var _second = 1000;
var _minute = _second * 60;
var _hour = _minute * 60;
var _day = _hour * 24;
var timer;

function serverTime() {
  var time = null;
  $.ajax({url: 'www.myserveradress.com/server_time.php',
      async: false, dataType: 'text',
      success: function(text) {
          time = new Date(text);
      }, error: function(http, message, exc) {
          time = new Date();
  }});
  return time;
}

function showRemaining() {
    var now = serverTime();
    var distance = end - now;
    if (distance < 0) {

        clearInterval(timer);
        window.location.href = '/surprise.html';

        return;
    }
    var days = Math.floor(distance / _day);
    var hours = Math.floor((distance % _day) / _hour);
    var minutes = Math.floor((distance % _hour) / _minute);
    var seconds = Math.floor((distance % _minute) / _second);

    document.getElementById('countdown').innerHTML = days + 'dagar ';
    document.getElementById('countdown').innerHTML += hours + 'timmar ';
    document.getElementById('countdown').innerHTML += minutes + 'minuter ';
    document.getElementById('countdown').innerHTML += seconds + 'sekunder';
}

timer = setInterval(showRemaining, 1000);

And the php script like this:

<?php
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Fri, 1 Jan 2010 00:00:00 GMT"); // Date in the past
header("Content-Type: text/plain; charset=utf-8"); // MIME type
$now = new DateTime();
echo $now->format("Y,m,d,H,i,s")."\n";
?>

Everything is working fine (the redirection, the countdown and the time zones) but what is not working is the server time.

It is a Frankenstein creation of different codes so I am pretty sure I have messed up somewhere, my guess is that I've messed up the serverTime function or in the showRemaining function (specifically how the now variabel should show the result of the serverTime function).


Solution

  • The function you're using does not work synchronously. The statement $.ajax will not wait for the fetch (as you might expect) and will pass on to the next statement immediately. When you return time, the variable time is not assigned yet.

    You should let the success callback of $.ajax to trigger update instead. When the fetch finish, jQuery.ajax will call that callback with the fetched result.

    var end = new Date(Date.UTC(2020,12-1,10,12,00,00)) ;
    var _second = 1000;
    var _minute = _second * 60;
    var _hour = _minute * 60;
    var _day = _hour * 24;
    var timer;
    
    // returns another callback for setInterval to use.
    function serverTime(callback) {
      // the below callback will be actually called on every setInterval trigger.
      return function () {
        var time = null;
        $.ajax({
            url: 'www.myserveradress.com/server_time.php',
            async: false, dataType: 'text',
            success: function(text) {
                time = new Date(text);
                callback(time); // ****** see this ********
            },
            error: function(http, message, exc) {
              time = new Date();
            },
        });
      }
    }
    
    function showRemaining(now) {
        if (now < end) {
            clearInterval(timer);
            window.location.href = '/surprise.html';
            return;
        }
    
        // gets milliseconds difference
        var distance = end.getTime() - now.getTime();
        var days = Math.floor(distance / _day);
        var hours = Math.floor((distance % _day) / _hour);
        var minutes = Math.floor((distance % _hour) / _minute);
        var seconds = Math.floor((distance % _minute) / _second);
    
        document.getElementById('countdown').innerHTML = days + 'dagar ';
        document.getElementById('countdown').innerHTML += hours + 'timmar ';
        document.getElementById('countdown').innerHTML += minutes + 'minuter ';
        document.getElementById('countdown').innerHTML += seconds + 'sekunder';
    }
    
    timer = setInterval(serverTime(showRemaining), 1000);
    

    Also, your server_time.php needs to return a string that Javascript's Date know how to read. E.g. RFC-2822 timestamp.

    <?php
    header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
    header("Expires: Fri, 1 Jan 2010 00:00:00 GMT"); // Date in the past
    header("Content-Type: text/plain; charset=utf-8"); // MIME type
    $now = new DateTime();
    
    echo $now->format(DateTimeInterface::RFC2822);
    

    Relevant documentation: