Click here to Skip to main content
Click here to Skip to main content

Tagged as

Accurate time in JavaScript

, 30 Jun 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Getting the accurate time in javascript from google server

Live Demo

Introduction

 

What if we want the accurate time when the client and server time are not accurate? The only workaround that I could find was json-time API. But json-time had a big Achilles Heel. That was json-time hosted on google and most of my clients couldn't call the API. 

I couldn't find any alternative API and I lost my hope, until I saw this comment in blog of James Padolseys:

Why not do a HTTP Head request to google.com to get the current time? I have used it in apps that need to check the correct time and never found different from ntp servers.

So I could find what I need in google.com header: 

I ilked the idea and I here is effort to access to accurqte time in javascript from google server

Writing the code step by step

Retrieving data by XMLHttpRequest and access to the response HEADER seems very easy to I write the first version:

xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", "http://www.google.com",true);
xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4) {
        alert(xmlhttp.getResponseHeader("Date"));
    }
}
xmlhttp.send(null);

But as a result I get NULL instead of date instead of date! I found the problem after viewing browser console log:

MLHttpRequest cannot load http://www.google.com/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fiddle.jshell.net' is therefore not allowed access. 

So I need URL with CORS support. I searched really hard but I couldn't find any good URL after days of searching. I changed my strategy and searched for another solution to capture the date header. Finally I found the solution. When I want to retrieve data from URL that does not exist, I recieve HTTP/1.1 404 Not Found error but this time I can read date header correctly. So I changed URL from http://www.google.com to http://www.googleapis.com:

xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", "http://www.googleapis.com",true);
xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4) {
        alert(xmlhttp.getResponseHeader("Date"));
    }
}
xmlhttp.send(null);​

OK now I have what I want but it's not a good way to send a request to another server any time I need time. We can calculate local time and server time diffrence and store it in local variable:

var TimeDiff;

xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", "http://www.googleapis.com",true);
xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4) {
        TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date());
        alert(TimeDiff);
    }
}
xmlhttp.send(null);

Or store it in localstorage:

var TimeDiffKey = 'Local-Server-TimeDiff';
var TimeDiff;

xmlhttp = new XMLHttpRequest();
xmlhttp.open("HEAD", "http://www.googleapis.com",true);
xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4) {
        TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date());
        
        window.localStorage.setItem(TimeDiffKey, TimeDiff);
    }
}
xmlhttp.send(null);

Storing TimeDiff in localstorage raises another problem, We need a mechanism to resync the diffrnce in timelly manner:

var SyncTimeframe = 1000 * 60 * 60 * 3; // 3 Hours
var LastSyncKey = 'LastSyncWithTimeServer';
var TimeDiffKey = 'Local-Server-TimeDiff';

if (window.localStorage.getItem(LastSyncKey) == null) {
    window.localStorage.setItem(LastSyncKey, '' + (new Date(0)));
}

LastSync = new Date(window.localStorage.getItem(LastSyncKey));

if ( Math.abs((new Date()) - LastSync) > SyncTimeframe) {
    SyncTime();
}
else {
    ShowTime();
}

function SyncTime() {
    xmlhttp = new XMLHttpRequest();
    xmlhttp.open("HEAD", "http://www.googleapis.com",true);
    xmlhttp.onreadystatechange=function() {
    
        if (xmlhttp.readyState==4) {
            TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date());
        
            window.localStorage.setItem(LastSyncKey, '' + (new Date()));
            window.localStorage.setItem(TimeDiffKey, TimeDiff);
            ShowTime();
        }
    }
    xmlhttp.send(null);
}

function ShowTime(){
        
    alert(window.localStorage.getItem(TimeDiffKey));
}

Network delay is the other concern, and to take care of it I add AcceptedDelay and RetryCount to code: 

var SyncTimeframe = 1000 * 60 * 60 * 3; // 3 Hours
var LastSyncKey = 'LastSyncWithTimeServer';
var TimeDiffKey = 'Local-Server-TimeDiff';

var RetryMax = 3;
var RetryCount = 0;
var AcceptedDelay = 500;

if (window.localStorage.getItem(LastSyncKey) == null) {
    window.localStorage.setItem(LastSyncKey, '' + (new Date(0)));
}

LastSync = new Date(window.localStorage.getItem(LastSyncKey));

if ( Math.abs((new Date()) - LastSync) > SyncTimeframe) {
    SyncTime();
}
else {
    ShowTime();
}

function SyncTime() {
    var StartTime = new Date();

    xmlhttp = new XMLHttpRequest();
    xmlhttp.open("HEAD", "http://www.googleapis.com",true);
    xmlhttp.onreadystatechange=function() {
    
        if (xmlhttp.readyState==4) {
            TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date());
        
            if (++RetryCount < 3 && (new Date()) - StartTime > AcceptedDelay) {
               SyncTime();
            }
            else {
                window.localStorage.setItem(LastSyncKey, '' + (new Date()));
                window.localStorage.setItem(TimeDiffKey, TimeDiff);
                ShowTime();
            }

        }
    }
    xmlhttp.send(null);
}

function ShowTime(){
        
    alert(window.localStorage.getItem(TimeDiffKey));
}

Also we can add the half of network delay to time diff to handle network delay better:

var SyncTimeframe = 1000 * 60 * 60 * 3; // 3 Hours
var LastSyncKey = 'LastSyncWithTimeServer';
var TimeDiffKey = 'Local-Server-TimeDiff';

var RetryMax = 3;
var RetryCount = 0;
var AcceptedDelay = 500;

if (window.localStorage.getItem(LastSyncKey) == null) {
    window.localStorage.setItem(LastSyncKey, '' + (new Date(0)));
}

LastSync = new Date(window.localStorage.getItem(LastSyncKey));

if ( Math.abs((new Date()) - LastSync) > SyncTimeframe) {
    SyncTime();
}
else {
    ShowTime();
}

function SyncTime() {
    var StartTime = new Date();

    xmlhttp = new XMLHttpRequest();
    xmlhttp.open("HEAD", "http://www.googleapis.com",true);
    xmlhttp.onreadystatechange=function() {
    
        if (xmlhttp.readyState==4) {
            TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date()) + ((new Date()) - StartTime) / 2;
        
            if (++RetryCount < 3 && (new Date()) - StartTime > AcceptedDelay) {
               SyncTime();
            }
            else {
                window.localStorage.setItem(LastSyncKey, '' + (new Date()));
                window.localStorage.setItem(TimeDiffKey, TimeDiff);
                ShowTime();
            }

        }
    }
    xmlhttp.send(null);
}

function ShowTime(){
        
    alert(window.localStorage.getItem(TimeDiffKey));
}

Using the code (Show the Date and Time) 

I want to show the Time in any DOM element with "RealServerTime" class, so I use document.getElementsByClassName to find all nodes:

var SyncTimeframe = 1000 * 60 * 60 * 3; // 3 Hours
var LastSyncKey = 'LastSyncWithTimeServer';
var TimeDiffKey = 'Local-Server-TimeDiff';

var RetryMax = 3;
var RetryCount = 0;
var AcceptedDelay = 500;

if (window.localStorage.getItem(LastSyncKey) == null) {
    window.localStorage.setItem(LastSyncKey, '' + (new Date(0)));
}

LastSync = new Date(window.localStorage.getItem(LastSyncKey));

if ( Math.abs((new Date()) - LastSync) > SyncTimeframe) {
    SyncTime();
}
else {
    ShowTime();
}

function SyncTime() {
    var StartTime = new Date();

    xmlhttp = new XMLHttpRequest();
    xmlhttp.open("HEAD", "http://www.googleapis.com",true);
    xmlhttp.onreadystatechange=function() {

        if (xmlhttp.readyState==4) {
            TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date()) + ((new Date()) - StartTime) / 2;

            if (++RetryCount < 3 && (new Date()) - StartTime > AcceptedDelay) {
               SyncTime();
            }
            else {
                window.localStorage.setItem(LastSyncKey, '' + (new Date()));
                window.localStorage.setItem(TimeDiffKey, TimeDiff);
                ShowTime();
            }

        }
    }
    xmlhttp.send(null);
}

function ShowTime(){
    var AllNodes=document.getElementsByClassName("RealServerTime");

    var diff = parseInt(window.localStorage.getItem(TimeDiffKey), 10);

    // format Date and Time
    var TimeToString=(new Date(Date.now() + diff)).toTimeString().split(' ')[0];

    for(var ipos=0;ipos<AllNodes.length;ipos++){
        AllNodes[ipos].innerHTML=TimeToString;
    }

    window.setTimeout(ShowTime, 1000);

}

The last step is to wrap all code in anonymous function to hide all local variable and functiuons:

 

(function () {

    var SyncTimeframe = 1000 * 60 * 60 * 3; // 3 Hours
    var LastSyncKey = 'LastSyncWithTimeServer';
    var TimeDiffKey = 'Local-Server-TimeDiff';

    var RetryMax = 3;
    var RetryCount = 0;
    var AcceptedDelay = 500;

    if (window.localStorage.getItem(LastSyncKey) == null) {
        window.localStorage.setItem(LastSyncKey, '' + (new Date(0)));
    }

    LastSync = new Date(window.localStorage.getItem(LastSyncKey));

    if ( Math.abs((new Date()) - LastSync) > SyncTimeframe) {
        SyncTime();
    }
    else {
        ShowTime();
    }

    function SyncTime() {
        var StartTime = new Date();

        xmlhttp = new XMLHttpRequest();
        xmlhttp.open("HEAD", "http://www.googleapis.com",true);
        xmlhttp.onreadystatechange=function() {
    
            if (xmlhttp.readyState==4) {
                TimeDiff=new Date(xmlhttp.getResponseHeader("Date")) - (new Date()) + ((new Date()) - StartTime) / 2;
        
                if (++RetryCount < 3 && (new Date()) - StartTime > AcceptedDelay) {
                   SyncTime();
                }
                else {
                    window.localStorage.setItem(LastSyncKey, '' + (new Date()));
                    window.localStorage.setItem(TimeDiffKey, TimeDiff);
                    ShowTime();
                }

            }
        }
        xmlhttp.send(null);
    }

    function ShowTime(){
        var AllNodes=document.getElementsByClassName("RealServerTime");
    
        var diff = parseInt(window.localStorage.getItem(TimeDiffKey), 10);
    
        // format Date and Time 
        var TimeToString=(new Date(Date.now() + diff)).toTimeString().split(' ')[0];
    
        for(var ipos=0;ipos<AllNodes.length;ipos++){
            AllNodes[ipos].innerHTML=TimeToString;
        }
    
        window.setTimeout(ShowTime, 1000);

    }
})();

Points of Interest

This is the first time I enjoy seeing HTTP/1.1 404 Not Found error.

If anyone know better solution to achieve a more accurate time I will be happy to hear it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Pooya Paridel
Team Leader Tehran Securities Exchange Technology Management C
Iran (Islamic Republic Of) Iran (Islamic Republic Of)
No Biography provided

Comments and Discussions

 
SuggestionMaybe you can try this Pinmemberrobert081530-Jun-14 1:59 
GeneralRe: Maybe you can try this PinmemberPooya Paridel30-Jun-14 4:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141030.1 | Last Updated 30 Jun 2014
Article Copyright 2014 by Pooya Paridel
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid