Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Normalize time on the client-side (javascript)

Tags:

javascript

Question:

How do you normalize client-side timestamps in javascript for users whose internal computer clocks are off? Note that I am dealing with time in UTC.

Context:

I have an AWS ElasticSearch instance set up with several batched and throttled operations along the way, making server-side timestamps unreliable (since the data could come in out-of-order, and the order matters). I therefore need to make my client-side timestamps more reliable.

Constraints:

I can't make any server-side requests (need to keep HTTP requests to a minimum), but I can include a server-side timestamp generated when my javascript is first loaded on the client.


Attempted solution:

Externally-defined variables:

  • serverTimestamp - a UTC timestamp (in milliseconds), generated server-side when the javascript is loaded.
  • getCookie - a function that gets a cookie value for a given key (or an empty string if not found).

Cache-control settings for the file are "public,max-age=300,must-revalidate" (so 5 minutes).

const getTimestamp = (function() {
    // This cookie is set on the `unload` event, and so should be greater than
    // the server-side timestamp when set.
    /** @type {!number} */
    const cookieTimestamp = parseInt(getCookie("timestamp_cookie"), 10) || 0;
    // This timestamp _should_ be a maximum of 5 minutes behind on page load
    // (cache lasts 5 min for this file).
    /** @type {!number} */
    const storedTimestamp = cookieTimestamp > serverTimestamp ?
                            cookieTimestamp : serverTimestamp;
    return function () {
        /** @type {!number} */
        const timestamp = Date.now();
        // This timestamp should be, at a *maximum*, 5-6 minutes behind
        // (assuming the user doesn't have caching issues)
        /** @type {!number} */
        const backupTimestamp = storedTimestamp
            + parseFloat(window.performance.now().toFixed(0));
        // Now let's check to see if the user's clock is
        // either too fast, or too slow:
        if (
            // Timestamp is smaller than the stored one.
            // This means the user's clock is too slow.
            timestamp < backupTimestamp
            // Timestamp is more than 6 minutes ahead. User's clock is too fast.
            // (Using 6 minutes instead of 5 to have 1 minute of padding)
            || (timestamp - backupTimestamp) > 360000
        ) {
            return backupTimestamp;
        } else {
            // Seems like the user's clock isn't too fast or too slow
            // (or just maximum 1 minute fast)
            return timestamp;
        }
    }
})();

Problem with solution:

Using the above getTimestamp function, running (new Date(getTimestamp())).getUTCDate() is returning the next day for some users, and getUTCHours seems to be all over the place in edge cases. I'm unable to diagnose the problem on my own.

like image 515
jperezov Avatar asked Dec 31 '19 00:12

jperezov


1 Answers

Stick to timestamps on the client side - don't bother with dates/times. Store the delta between the serverside and the client clock on the client. Send the client timestamp and delta with the messages then reconstruct the date/time on the server.

Then you can make sensible decisions serverside about how to deal with jitter and jumps in the clock sync.

like image 153
symcbean Avatar answered Sep 28 '22 00:09

symcbean