Dmitry Bubnenkov
Dmitry Bubnenkov

Reputation: 9859

Why new Date().toISOString() is loosing timezone?

I do not want to use any external libs like momentjs, I want to create a formatted date string myself. I tried to use new Date().toISOString() but it's losing time-zone.

This:

new Date()

Gives:

Sat Jun 24 2017 09:32:10 GMT+0300 (RTZ 2 (winter))

And:

new Date().toISOString();

Gives:

2017-06-24T06:32:22.990Z

And 09:32:10 is the right time, so 06:32:22 hass lost timezone information.

To add to this, it looks like new Date().toLocaleString() does almost what I need. At last hours is correct. Result: "24.06.2017, 11:37:05".

Upvotes: 8

Views: 11669

Answers (3)

Tom
Tom

Reputation: 17854

In the updated Question you propose using toLocaleString(), and yes this will get the date into the users' current locale (vs. toISOString which always uses GMT), but you've lost the ISO 8601 formatting.

The solution that I use is to set the locale to a country that uses 8601, eg.

const d = new Date();
const dateStr = d.toLocaleString( 'sv' );
// locale 'sv' is Sweden so you get ISO 8601 format

The result will have a space instead of the 'T' but that is legal in RFC 3339.

[edit: clarified that is is RFC 3339 that allows the space, as per T.J. Crowder comment.]

Upvotes: 4

T.J. Crowder
T.J. Crowder

Reputation: 1074168

JavaScript Date objects don't store timezone information. They use the system timezone, and that's it. So once you have a Date, you've lost timezone information (other than the current system's timezone).

From a Date, you can get the current timezone offset, and you can form a string in ISO-8601 format using that timezone offset:

function pad(x, width = 2, char = "0") {
    return String(x).padStart(width, char);
}
function toLocalISOString(dt) {
    const offset = dt.getTimezoneOffset();
    const absOffset = Math.abs(offset);
    const offHours = Math.floor(absOffset / 60);
    const offStr = pad(offHours) + ":" + pad(absOffset - offHours * 60);
    return [
        String(dt.getFullYear()),
        "-",
        pad(dt.getMonth() + 1),
        "-",
        pad(dt.getDate()),
        "T",
        pad(dt.getHours()),
        ":",
        pad(dt.getMinutes()),
        ":",
        pad(dt.getSeconds()),
        ".",
        dt.getMilliseconds(),
        offset <= 0 ? "+" : "-",
        offStr
    ].join("");
}
console.log(toLocalISOString(new Date()));

...but an offset isn't a timezone; an offset only tells you how many hours and minutes offset from UTC a date/time is, not what rules govern it.

In any modern browser (not IE11), you can get the actual timezone from Intl.DateTimeFormat().resolvedOptions().timeZone, which will give you an IANA string like "Europe/London". You could include that in your custom string, perhaps conditionally based on being able to get it:

function pad(x, width = 2, char = "0") {
    return String(x).padStart(width, char);
}
function toLocalISOString(dt) {
    const offset = dt.getTimezoneOffset();
    const absOffset = Math.abs(offset);
    const offHours = Math.floor(absOffset / 60);
    const offStr = pad(offHours) + ":" + pad(absOffset - offHours * 60);
    let parts = [
        String(dt.getFullYear()),
        "-",
        pad(dt.getMonth() + 1),
        "-",
        pad(dt.getDate()),
        "T",
        pad(dt.getHours()),
        ":",
        pad(dt.getMinutes()),
        ":",
        pad(dt.getSeconds()),
        ".",
        dt.getMilliseconds(),
        offset <= 0 ? "+" : "-",
        offStr
    ];
    if (typeof Intl === "object" && Intl.DateTimeFormat) {
        try {
            parts.push(
                " (",
                Intl.DateTimeFormat().resolvedOptions().timeZone,
                ")"
            );
        } catch (e) {
        }
    }
    return parts.join("");
}
console.log(toLocalISOString(new Date()));

Upvotes: 1

Shahar Kazaz
Shahar Kazaz

Reputation: 1206

Answer from a similar question

moment.js is great but sometimes you don't want to pull a lage number of dependencies for a simple things.

the following works as well:

var tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds var localISOTime = (new Date(Date.now() - tzoffset)).toISOString().slice(0,-1); // => '2015-01-26T06:40:36.181'

The slice(0,-1) gets rid of the trailing Z which represents Zulu timezone and can be replaced by your own.

Upvotes: 3

Related Questions