37

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);
Basj
  • 41,386
  • 99
  • 383
  • 673
  • Probably a duplicate of [*Where can I find documentation on formatting a date in JavaScript?*](https://stackoverflow.com/questions/1056728/where-can-i-find-documentation-on-formatting-a-date-in-javascript) – RobG Mar 17 '18 at 02:26

4 Answers4

37

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()));
RobG
  • 142,382
  • 31
  • 172
  • 209
  • See this answer for a more detailed explanation of each operation: https://stackoverflow.com/a/51643788/1412157 – LucaM Nov 07 '19 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 Jun 06 '21 at 01: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 Jun 07 '21 at 07: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`. – Maciej Krawczyk Oct 03 '21 at 15:22
11

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
Manngo
  • 14,066
  • 10
  • 88
  • 110
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()));
mplungjan
  • 169,008
  • 28
  • 173
  • 236
rofrol
  • 14,438
  • 7
  • 79
  • 77
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

Mr Shane
  • 520
  • 5
  • 18
  • 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](/help/how-to-answer). – Community Oct 19 '21 at 16:09
  • The first answer will only work on your computer since not everyone is in the `GMT+2` time zone – Alexandre Senges Oct 19 '21 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 Oct 19 '21 at 18:40
  • 1
    toLocaleString("sv-SE") is actually interesting, it's just the format and local time is still intact – Dee Jun 17 '22 at 16:32