0

I am using following logic to convert UTC date time (coming from server in this format 2017-01-25T23:08:08.453) into local date time on browser using JavaScript(without asking user to input his timezone detail & assuming system date time/timezone on client machine is right).

 var jsDate = new Date(do.createdAt);
 var jsDate = new Date(jsDate.getTime() - jsDate.getTimezoneOffset() * 60000);

The login seems to be working fine in IE but in chrome I am getting different result.

Execute this fiddle in IE and Chrome to see different results.

If I use moment.js I am getting correct result in both IE and chrome:

moment.utc(do.createdAt).toDate();

But I do not want to introduce additional dependency.

I looked at multiple related post on SO but every post has multiple comments pointing out the drawbacks of each approach.

What is the best way to proceed assuming local timezone on client machine is correct.

SharpCoder
  • 18,279
  • 43
  • 153
  • 249
  • 1
    Impossible to say without knowing the value of *do.createdAt*. But if it's an ISO 8601 extended format string like "2017-01-25T12:00:00+0000" then it *should* be parsed correctly (by browsers consistent with ECMAScript ed 5 and later) without any further help. – RobG Jan 25 '17 at 23:38
  • @RobG: I am getting date in this format from server `2017-01-25T23:08:08.453` – SharpCoder Jan 26 '17 at 16:02
  • @RobG: Updated the question and included the fiddle – SharpCoder Jan 26 '17 at 22:37

1 Answers1

0

Assuming that the "UTC date time" is an ISO 8601 format string and that you need to mess with the offset then likely it doesn't have an offset in the string. Also, if you're getting different results in different browsers then likely it's not strict ISO 8601, e.g. something like:

"2017-01-25 12:00:00"

which is missing the "T" separator and the timezone. You can parse the string and add the missing T and timezone (Z will do) and then leave it to the built–in parser, you can skip a step and parse it directly to a date yourself, or give it to a parsing library (plenty to choose from).

To parse an ISO 8601 like string per the above as UTC, the following will do the job:

/* Parse a string without timezone like "2017-01-25 12:00:00" as UTC
** @param {string} s - string to parse
** @returns {Date} returns an invalid date if any
**                 part is out of range.
*/
function parseAsUTC(s) {
  var b = s.split(/\D/);
  var d = new Date(Date.UTC(b[0], --b[1], b[2], b[3], b[4], b[5], b[6]||0));
  return d &&  // generated an object
         d.getUTCMonth() == b[1] &&  // month didn't roll over
         d.getUTCHours() == b[3] &&  // hour didn't roll over
         d.getUTCSeconds() == b[5] ? // seconds didn't roll over
         d : new Date(NaN);
}

console.log(parseAsUTC('2017-01-25 12:00:00')); // Valid date
console.log(parseAsUTC('2017-01-32 12:00:00')); // Invalid date

console.log(parseAsUTC('2017-01-25T23:08:08.453')); // 2017-01-25T23:08:08.453Z

It's only necessary to check some of the date parts as if it didn't change, then it and its next lowest unit must be in range, and if either was out of range, the higher unit would change due to either rolling over itself or the next highest unit.

Edit

If an ISO 8601 date string is missing a timezone, i.e. like "2017-01-25T23:08:08.453", then if parsed using the built–in parser, it should be treated as local. However, parsing strings with the Date constructor (or Date.parse, they are equivalent for parsing) is notoriously unreliable.

So no change to the above advice, and the parseAsUTC function takes it in its stride. :-)

BTW, in your code:

var jsDate = new Date('2017-01-25T23:08:08.453');
var jsDate = new Date(jsDate.getTime() - jsDate.getTimezoneOffset() * 60000);

you can apply the timezone offset directly using setMinutes to avoid creating an unnecessary additional Date. But again, it relies on the built–in parser so stick to the supplied function or a library:

var s = '2017-01-25T23:08:08.453';
var d = new Date(s);
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());

console.log('Date string  : ' + s +
          '\nAdjusted date: ' + d.toISOString());
RobG
  • 142,382
  • 31
  • 172
  • 209