0

I have an app that displays data to users based on the date. I send an array from the server to the client, on load, which contains 3 objects with some data - effectively an object for 'yesterday', 'today' and 'tomorrow' in UTC, since at most there can be 3 different dates across the world. Each object contains some data for that date.

E.g:

[
 {dateStamp: '2022-05-31', data: 'some data for 31 May'},
 {dateStamp: '2022-06-01', data: 'some data for 1 Jun'},
 {dateStamp: '2022-06-02', data: 'some data for 2 Jun'}
]

In JS, I then decide what timezone the client is in and pick the appropriate object to show them (UTC-1 day, UTC today, UTC+1 day). I do this based on a simple Date object. From that object I generate a timestamp string (YYYY-MM-DD) and then match it against the 3 objects' timestamp strings (generated in PHP with the same format).

But I'm noticing in a really odd error in my activity logs. (This is data I post from clients via AJAX and store on the server for monitoring/debugging). I first spotted it when some users were getting shown data for tomorrow.

Some users (not many) are reporting UNIX timestamps (from Date.now()) that are in the future (whilst appearing to report a UTC offset that is correct).

The reported UTC offsets are generated in JS at the client and sent via AJAX as such:

let nowDate = new Date();
let utcOffset = nowDate.getTimezoneOffset();
if (!isEmpty(utcOffset) && !isNaN(utcOffset)) { // isEmpty is my own function to match the PHP empty() function
   utcOffset = (-1 * utcOffset) / 60;
}
else {
   utcOffset = undefined;
}

Now obviously this data is coming from client, so a malicious user could simply use the console to manipulate this to anything they like as it's being sent.

But it's a small app with relatively few users and that seems extremely unlikely to me.

So, excluding a malicious user, some clients apparently have their local clock set wrong (set to the future). Why?? Or how?? How are they in the right timezone but the wrong clock time by several days? This is not an app that attracts 1000s of users - I have 100s and I pretty much know they're all accessing via fairly normal means, primarily via mobiles - I know it's an assumption but I would have expected their clocks to be (approximately) correct.

What's the best way of checking what the actual time in their timezone is? I'm thinking I send the UTC offset to the server via AJAX and my server reports back what the date is in that timezone?


For completeness, here is what/how I'm sending to the server from the client:

function getDateStamp() { // Aware this function is verbose, haven't had time to improve
    let now = new Date();
    let todayDay = now.getDate().toString();
    let todayMonth = (now.getMonth() + 1).toString();
    let todayYear = now.getFullYear().toString();
    if (!isEmpty(todayDay) && todayDay.length < 2) {
        todayDay = "0" + todayDay;
    }
    if (!isEmpty(todayMonth) && todayMonth.length < 2) {
        todayMonth = "0" + todayMonth;
    }
    let fullDateStamp = todayYear + "-" + todayMonth + "-" + todayDay;
    if (!isEmpty(todayYear) && !isEmpty(todayMonth) && !isEmpty(todayDay)) {
        return fullDateStamp;
    }
    else {
        return false;
    }
}

let clientDateStamp = getDateStamp();
let clientUnixTimestamp = Math.floor(Date.now() / 1000);

let objectToSend = {dateStamp: clientDateStamp, unixTimestamp: clientUnixTimestamp};

var dataTransfer = $.ajax({ // Using JQuery for AJAX post
  url: serverEndpointURL, // Where serverEndpointURL is an HTTPS url to my server script
  type: "POST",
  dataType: "json",
  data: {json: objectToSend}
});

In PHP, on the server I decode that JSON:

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
   $json = $_POST["json"];
   $postData = json_decode($json, true);
}
error_log($postData["unixTimestamp"] . "\r\n",3, $errorLogFile); // $errorLogFile points to a txt file

In $errorLogFile I can see timestamps in the future. I.e. $postData["unixTimestamp"] > time() would have been true at the time of logging.

David_0106
  • 79
  • 7
  • 2
    You say that `Date.now()` is in the future but you don't show any code example where you actually use that value. Which exact variable is in the future? – Álvaro González Jun 01 '22 at 15:20
  • 2
    What's all this song and dance with sending three dates from the server and then picking one from the client?! That seems overly complicated for… unknown benefit. Just generate `new Date` on the client and send it to the server, or use it on the client? — And yes, clients *will* have the wrong date sometimes, that's just a fact. But it's unclear whether your approach is simply overcomplicated and you have a bug in it, or whether the client's clock is really the issue. There isn't enough reproducible code here to tell for sure. – deceze Jun 01 '22 at 15:20
  • Check your logs, or log more details. Browser(s) and/or IP address of the user(s) should help you. – user3783243 Jun 01 '22 at 15:20
  • 1
    Side note, you can make the date stamp function a lot simpler. See https://stackoverflow.com/a/29774197/6911703 (make sure to use the second part so you preserve the time zone). – asportnoy Jun 01 '22 at 15:21
  • You can simply use ISO date and slice to get formated date: `new Date().toISOString().slice(0, 10)` – xdeepakv Jun 01 '22 at 15:31
  • No need for such complex code to fomat. U can also use str.padStart(2, "0") to fill with zero. – xdeepakv Jun 01 '22 at 15:32
  • @xdeepakv that will change the date into UTC. See the link I posted above to get it in their time zone. – asportnoy Jun 01 '22 at 15:36
  • @deceze There's a good reason for sending the three objects on load - I need to have those objects ready to use as soon as possible and don't want to have to send an ajax call back to the server. Using JS/client for client's time seems better to me than trying to do this in PHP. – David_0106 Jun 01 '22 at 15:44
  • @Álvaro González I'll update the question. I specifically send `Math.floor(Date.now() / 1000)` to the server. By in future, I mean that value is `> time()` on the server in PHP. – David_0106 Jun 01 '22 at 15:47
  • @asportnoy Thanks for the link. I didn't write the `getDateStamp()` function and it hasn't been a priority to fix, but I will do...! – David_0106 Jun 01 '22 at 16:04
  • 1
    Unix time is a fixed moment in time, it isn't affected by time zones... If it's wrong, then either the visitor's computer clock is wrong or you're doing manipulations that involve a conversion to a local time. If you just print the value as is into your log file, it must be the former. If the difference is a multiple of 60, a possible clock mistake is having an incorrect time zone set up. – Álvaro González Jun 02 '22 at 06:42
  • @Álvaro González They seemed to be wrong by multiples of 24hrs, i.e. +1 or +2 days. I don't understand how an incorrect time zone could effect the clock time though? As you say, isn't this a fixed moment in time independent of their timezone? – David_0106 Jun 03 '22 at 12:33
  • Then it could be something as simple as a wrong date. Hard to say. – Álvaro González Jun 03 '22 at 15:53

0 Answers0