41

Let's say we have this datetime:

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");

Exporting it as a string (console.log(d)) gives inconsistent results among browsers:

  • Sat Jul 21 2018 14:00:00 GMT+0200 (Paris, Madrid (heure d’été)) with Chrome

  • Sat Jul 21 14:00:00 UTC+0200 2018 with Internet Explorer, etc.

so we can't send datetime to a server with an unconsistent format.

The natural idea then would be to ask for an ISO8601 datetime, and use d.toISOString(); but it gives the UTC datetime: 2018-07-21T12:00:00.000Z whereas I would like the local-timezone time instead:

2018-07-21T14:00:00+0200
or
2018-07-21T14:00:00

How to get this (without relying on a third party dependency like momentjs)?

I tried this, which seems to work, but isn't there a more natural way to do it?

var pad = function(i) { return (i < 10) ? '0' + i : i; };

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
Y = d.getFullYear();
m = d.getMonth() + 1;
D = d.getDate();
H = d.getHours();
M = d.getMinutes();
S = d.getSeconds();
s = Y + '-' +  pad(m) + '-' + pad(D) + 'T' + pad(H) + ':' + pad(M) + ':' + pad(S);
console.log(s);

1

4 Answers 4

38

There is limited built-in support for formatting date strings with timezones in ECMA-262, there is either implementation dependent toString and toLocaleString methods or toISOString, which is always UTC. It would be good if toISOString allowed a parameter to specify UTC or local offset (where the default is UTC).

Writing your own function to generate an ISO 8601 compliant timestamp with local offset isn't difficult:

function toISOLocal(d) {
  var z  = n =>  ('0' + n).slice(-2);
  var zz = n => ('00' + n).slice(-3);
  var off = d.getTimezoneOffset();
  var sign = off > 0? '-' : '+';
  off = Math.abs(off);

  return d.getFullYear() + '-'
         + z(d.getMonth()+1) + '-' +
         z(d.getDate()) + 'T' +
         z(d.getHours()) + ':'  + 
         z(d.getMinutes()) + ':' +
         z(d.getSeconds()) + '.' +
         zz(d.getMilliseconds()) +
         sign + z(off/60|0) + ':' + z(off%60); 
}

console.log(toISOLocal(new Date()));

6
  • See this answer for a more detailed explanation of each operation: stackoverflow.com/a/51643788/1412157
    – LucaM
    Commented Nov 7, 2019 at 10:26
  • @LucaM—that answer is an entirely different way of getting a timestamp that doesn't explain the above method. It also doesn't include the offset.
    – RobG
    Commented Jun 6, 2021 at 1:08
  • Hi @RobG, I always prefer solutions that explain in details each step. That's why I linked that answer. If you could include some comments in your snippet I think it'd really improve the quality of your answer. (just my 2 cents)
    – LucaM
    Commented Jun 7, 2021 at 7:59
  • 4
    The code is pretty simple. The only unknown was the use of bitwise operator to floor the offset. I think it would be better off with a Math.floor(off/60) rather than off/60|0. Commented Oct 3, 2021 at 15:22
  • This answer has the same problem as the other one: try it with new Date("Sat Jul 21 2018 22:00:00 GMT-0800") and you end up skipping a day. In my current timezone this results in 2018-07-22T01:00:00.000-05:00.
    – atoth
    Commented Oct 6, 2023 at 10:55
14

The trick is to adjust the time by the timezone, and then use toISOString(). You can do this by creating a new date with the original time and subtracting by the timezone offssetfrom the original time:

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
var newd = new Date(d.getTime() - d.getTimezoneOffset()*60000);
console.log(newd.toISOString());    //  2018-07-21T22:00:00.000Z

Alternatively, you can simply adjust the original date variable:

var d = new Date("Sat Jul 21 2018 14:00:00 GMT+0200");
d.setTime(d.getTime() - d.getTimezoneOffset()*60000);
console.log(d.toISOString());       //  2018-07-21T22:00:00.000Z

Note, however, that adjusting the original date this way will affect all of the date methods.

For your convenience, the result from .getTime() is the number of milliseconds since 1 January 1970. However, getTimezoneOffset() gives a time zone difference from UTC in minutes; that’s why you need to multiply by 60000 to get this in milliseconds.

Of course, the new time is still relative to UTC, so you’ll have to ignore the Z at the end:

d = d.slice(0,-1);                  //  2018-07-21T22:00:00.000
1
  • Setting the date to "Sat Jul 21 2018 22:00:00 GMT-0800" would produce 2018-07-22T01:00:00.000Z. We jumped a day. (I am currently trying to find the sure fire version for this problem).
    – atoth
    Commented Oct 6, 2023 at 10:53
1

My version:

// https://stackoverflow.com/questions/10830357/javascript-toisostring-ignores-timezone-offset/37661393#37661393
// https://stackoverflow.com/questions/49330139/date-toisostring-but-local-time-instead-of-utc/49332027#49332027
function toISOLocal(d) {
  const z = n => ('0' + n).slice(-2);
  let off = d.getTimezoneOffset();
  const sign = off < 0 ? '+' : '-';
  off = Math.abs(off);
  return new Date(d.getTime() - (d.getTimezoneOffset() * 60000)).toISOString().slice(0, -1) + sign + z(off / 60 | 0) + ':' + z(off % 60);
}
console.log(toISOLocal(new Date()));

0

i have found a solution which has worked for me.

see this post: Modifying an ISO Date in Javascript

for myself i tested this with slight modification to remove the "T", and it is working. here is the code i am using:

// Create date at UMT-0
  var date = new Date();
// Modify the UMT + 2 hours
  date.setHours(date.getHours() + 2);
// Reformat the timestamp without the "T", as YYYY-MM-DD hh:mm:ss
  var timestamp = date.toISOString().replace("T", " ").split(".")[0];

and an alternative method is stipulate the format you need, like this:

// Create the timestamp format
  var timeStamp = Utilities.formatDate(new Date(), "GMT+2", "yyyy-MM-dd' 'HH:mm:ss");

note: these are suitable in locations that do not have daylight saving changes to the time during the year

it has been pointed out that the above formulas are for a specific timezone.

in order to have the local time in ISO format, first specify suitable Locale ("sv-SE" is the closest and easiest to modify), then make modification (change the space to a T) to be same as ISO format. like this:

var date = new Date(); // Create date
var timestamp = date.toLocaleString("sv-SE").replace(" ", "T").split(".")[0]; // Reformat the Locale timestamp ISO YYYY-MM-DDThh:mm:ss

References:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString)

https://www.w3schools.com/Jsref/jsref_tolocalestring.asp

https://www.w3schools.com/Jsref/tryit.asp?filename=tryjsref_tolocalestring_date_all

4
  • Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Oct 19, 2021 at 16:09
  • The first answer will only work on your computer since not everyone is in the GMT+2 time zone Commented Oct 19, 2021 at 16:10
  • 1
    the OP made mention of GMT+2, so the initial solutions were provided on that basis, however i have now added solution which will return the local date and time based on the users timezone wherever they are, and in the ISO format.
    – Mr Shane
    Commented Oct 19, 2021 at 18:40
  • 1
    toLocaleString("sv-SE") is actually interesting, it's just the format and local time is still intact
    – Dan D.
    Commented Jun 17, 2022 at 16:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.