0

We have a date formatting function that seems to be producing different dates depending on browser. The function has two steps, first to determine the user's date format, the second to format the date accordingly.

// determine date string format for PrettyDate functions
var dtstr;
var dtsep;
let customDate = new Date(2222, 11, 18);
let strDate = customDate.toLocaleDateString();
let daTyp = strDate.substring(0, 2);
if (daTyp === '22') {
  dtstr = 'YMD';
  dtsep = ',';
}
else if (daTyp === '12') {
  dtstr = 'MDY';
  dtsep = ';';
}
else {
  dtstr = 'DMY';
  dtsep = ',';
}

// make dates human readable
function prettyDate(datestr, use) {
  var date = new Date(datestr);
  if (!isNaN(date.getTime())) {
    const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
    const monthCount = date.getMonth();
    const theMonth = monthNames[monthCount];
    const theYear = date.getFullYear();
    const theDay = date.getDate();
    if (use === 'short') {
      if (dtstr === 'MDY') {
        return theMonth + ' ' + theDay;
      }
      else {
        return theDay + ' ' + theMonth;
      }
    }
    else {
      if (dtstr === 'YMD') {
        return datestr;
      }
      else if (dtstr === 'MDY') {
        return theMonth + ' ' + theDay + ', ' + theYear;
      }
      else {
        return theDay + ' ' + theMonth + ' ' + theYear;
          }
    }
  }
}

The datestr being converted is of the format 2022-08-17 and the use value is either 'short' or 'long'.

All the checks we have done on computers we have access to show the end date as 17 Aug 2022 or Aug 17 2022. But we have had several website users report they are getting 16 Aug 2022 or Aug 16 2022.

UPDATE

Have tried some experiments with computer timezone and there does seem to a be an affect. So the new pressing question is how do we modify the code to prevent the OS timezone from affecting the result?

Further experimenting with computer timezone setting has shown that if the timezone is UTC +XX:00, then the date displayed is correct. If it is UTC -XX:00, then the date is one day earlier.

FINALLY

I officially award myself the Dunderhead Award of the day. My datestr is basically a string, so just use split() and reform the parts. What a over-engineered noob move.

function prettyDate(datestr, use) {
  const dtparts = String(datestr).split('-');
  const monthNames = ["","Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
  const monthCount = +dtparts[1];
  const theMonth = monthNames[monthCount];
  const theYear = dtparts[0];
  const theDay = dtparts[2];
  ... previous code
}

Thanks to those who tried to help.

Tom
  • 2,928
  • 4
  • 28
  • 36
  • 3
    Are you sure this isn’t having anything to do with the time zones of the machines you’re testing on concurrently? – esqew Jan 26 '22 at 03:07
  • @esqew - How would the timezone affect the JS? The `datestr` value comes from a database on our server. – Tom Jan 26 '22 at 03:34
  • Are you using the same browser? See this post: https://stackoverflow.com/questions/3552461/how-to-format-a-javascript-date – code Jan 26 '22 at 03:34
  • @code - I have tried multiple browsers successfully. One website visitor who reported the issue, has tried a couple of browsers on Win 11, Edge which showed the correct date, Firefox which showed the incorrect date and on Win10 with Edge showing the incorrect date. It is a stumper.. – Tom Jan 26 '22 at 03:42
  • Not sure if it has anything to do with the `datestr` format, but try formatting them to `2022/08/17` instead of using hyphens `-`, because that seems to have some parsing issues in some browsers: https://dygraphs.com/date-formats.html – Vicky Jan 26 '22 at 03:56
  • @Vicky - will look at that angle, but pray it is not the issue as that would mean changing hundreds of dates in the database and other code that uses said dates. – Tom Jan 26 '22 at 04:17
  • Try logging `daTyp`. – code Jan 26 '22 at 04:23
  • The format determining code that creates and uses `daTyp` seems to be working OK, as the folks are seeing dates in the correct local format. It seems to be the creation of `theDate` in the `prettyDate` function that is being messed up. – Tom Jan 26 '22 at 04:44
  • @Tom or you could just replace it with javascript `datestr.replace(/\-/g, '/')` – Vicky Jan 26 '22 at 05:37
  • @Tom Have you determined whether the discrepancy is in the `datestr` value retrieved by the browser, as opposed to incorrect parsing of that value? – phatfingers Jan 26 '22 at 06:09
  • @Tom If it turns out that `datestr` itself is the culprit, how, as specifically as possible, is that string produced by the server? Is the database a date, datetime, or timestamp? Do you have more than one server? Does the server use its own timezone to render the date or one somehow associated with the user? – phatfingers Jan 26 '22 at 06:27
  • @phatfingers - the `datestr` was manually entered into the database, not created by the server. It is requested via an API and returned as part of a json file in the format of `"tourdate":"2022-08-17",` and then processed via `prettyDate(data[i].dates[j].tourdate, 'long')` – Tom Jan 26 '22 at 06:40
  • @Tom To be certain, you're saying the datestr is stored in the database as a string? – phatfingers Jan 26 '22 at 07:01
  • @Tom What is typical, albeit not the only possibility, is that your API is just a protocol for mapping requests to code on your server. When that code on your server fetches a date from a database, it uses the server's time zone when it prints that date to a string, such as for your JSON output. If that date lives on your database as a DATE, DATETIME or TIMESTAMP, then the server may be influenced by timezone when it produces the JSON. If the date is in a CHAR, VARCHAR, or TEXT field on the database, then you can rule that out. – phatfingers Jan 26 '22 at 07:11
  • @Tom If you have multiple servers, for redundancy or performance, it's possible for those servers to be configured for different time zones. Each could retrieve the same DATE from a database and produce a different string for their JSON output. – phatfingers Jan 26 '22 at 07:18
  • @phatfingers - The string I put in my previous comment is copied directly from the json file and apologies perhaps API was the wrong word to use. The server produces a static file each night with all the data in json format. The page JS fetches that file and extracts the data needed for each function. So each browser gets the exact same data on any given day. So am fairly certain it is not the data received but rather the way the browser processes it. – Tom Jan 26 '22 at 07:42

1 Answers1

1

If the initial date string is in the format "YYYY-MM-DD", the if parsed using:

new Date(datestr)

it will be parsed as UTC, so anyone with a negative offset (i.e. west of Greenwich) the date will be the day prior. That issue is covered in Why does Date.parse give incorrect results?

Also, it doesn't seem like a good idea to attempt to determine the format of toLocaleString then mimic the format. Just use toLocaleString with suitable options, that's how it's supposed to be used. However, you can't be certain of the format that will be presented to the user as it's implementation dependent (and assumes the user has set their browser or system language appopriately).

If the date string should be parsed as local, then do that manually as part of reformatting, e.g.

// Parse timestamp in YYYY-MM-DD format as local
// and format as unambiguous timestamp with short month name
function reformatTimestamp(s, lang = 'default') {

  // Parse timestamp as local
  let [y, m, d] = s.split(/\D/);

  // Fromat with short month name so unambiguous
  return new Date(y, m-1, d).toLocaleString(lang, {
    year:'numeric', month:'short', day:'numeric'});
}

// Results for different languages:
//   en-GB : 22 Jan 2022
//   en-US : Jan 222, 2022
//   fr    : 22 janv. 2022
//   ar    : ٢٢ يناير، ٢٠٢٢
console.log(reformatTimestamp('2022-01-22'));

The above takes an optional language parameter that is a BCP47 language tag )(often mistakenly called "locale").

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Thanks for the clarification on `date()` parsing to UTC, that explains it. We really didn't want to go full on with internationalization, as the website is in English only. We just wanted to arrange the D M Y sequence to be familiar. – Tom Jan 27 '22 at 04:06
  • Simply using an unambiguous format is sufficient, e.g. neither Jan 22, 2022 nor 22 Jan 2022 are going to be misunderstood. People like to make a big fuss over date formats but really it's such a minor issue. E.g. en-GB is supposed to be d/m/y, but [*The Times* newspaper](https://www.thetimes.co.uk), which is about as British establishment as you can get, uses "Thursday January 27 2022" and has for centuries (337 years and counting…). – RobG Jan 27 '22 at 22:51
  • RobG yes it is a minor issue, but sometimes it is the little things that help push someone from being a looker to a booker (it is for a travel site :). – Tom Jan 29 '22 at 08:23