62

how do I format a javascript date like ISO format, but in local time?

with myDate.toISOString() I am getting the time as: "2012-09-13T19:12:23.826Z"

but here, it is 22:13, so how do I include the timezone in above format?


I ended up doing...

pad=function(e,t,n){n=n||"0",t=t||2;while((""+e).length<t)e=n+e;return e}
c = new Date()
c.getFullYear()+"-"+pad(c.getMonth()+1)+"-"+pad(c.getDate()-5)+"T"+c.toLocaleTimeString().replace(/\D/g,':')+"."+pad(c.getMilliseconds(),3)
Billy Moon
  • 57,113
  • 24
  • 136
  • 237

13 Answers13

59

No library required! For some Date object, e.g. t = new Date()

  • convert the local time zone offset from minutes to milliseconds

    z = t.getTimezoneOffset() * 60 * 1000

  • subtract the offset from t

    tLocal = t-z

  • create shifted Date object

    tLocal = new Date(tLocal)

  • convert to ISO format string

    iso = tLocal.toISOString()

  • drop the milliseconds and zone

    iso = iso.split(".")[0]

  • replace the ugly 'T' with a space

    iso = iso.replace('T', ' ')

Result is a nice ISO-ish format date-time string like "2018-08-01 22:45:50" in the local time zone.

Denis Howe
  • 2,092
  • 1
  • 23
  • 25
  • 4
    Note that the offset is positive if the local timezone is behind UTC, and negative if it is ahead. That's why you have to subtract it and not add it. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset – e18r Jun 28 '20 at 00:13
  • Just to add: If you want to read again final `iso` as Date Object: `newD = new Date(Date.parse(iso));` – Om Sao Oct 20 '20 at 01:30
  • @OmSao—using *Date.parse* in that expression is redundant, `new Date(iso);` will produce an identical result. – RobG Mar 20 '21 at 22:05
  • `iso.slice(0, 19)` is flawed. See the [documentation](//developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString). Try a 5-digit year: this will require `iso.slice(0, 22)`. It’s better to just use `iso.split(".")[0]`. – Sebastian Simon Oct 29 '22 at 03:49
49

A bit of a hack but can be done in one line by taking advantage of the fact that Sweden uses a format very close to ISO:

// Returns a string like 2021-01-17T01:59:57
function dateToISOButLocal(date) {
    return date.toLocaleString('sv').replace(' ', 'T');
}

To support milliseconds:

return date.toLocaleString('sv', {year:'numeric', month:'numeric', day:'numeric', hour:'numeric', minute:'numeric', second:'numeric', fractionalSecondDigits: 3}).replace(',', '.').replace(' ', 'T');
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
AlliterativeAlice
  • 11,841
  • 9
  • 52
  • 69
  • 4
    You don't even need the `replace()` any more: `date.toLocaleDateString('sv')`. Also, this hack is really nice if you need the date for a particular time zone: `date.toLocaleDateString('sv', {timeZone: 'America/New_York'})` – Alec Jul 12 '21 at 13:53
  • I love this! Simple and clean and I don't frankly care about looks as long as it works. – TeemuK Sep 25 '21 at 08:58
  • 1
    Consider `....toLocaleString('sv',{timeZoneName:'short'}).replace(' GMT','')` to add the local offset. ;-) – RobG Feb 25 '22 at 23:40
23

I went with what Denis Howe said, below as a ready made function for convenience.

Also one fix: in the original answer t-z does not work because t is a Date, not milliseconds.

function dateToISOLikeButLocal(date) {
    const offsetMs = date.getTimezoneOffset() * 60 * 1000;
    const msLocal =  date.getTime() - offsetMs;
    const dateLocal = new Date(msLocal);
    const iso = dateLocal.toISOString();
    const isoLocal = iso.slice(0, 19);
    return isoLocal;
}

With this I get the kind of string that needed as a URL parameter:

"2018-11-16T12:23:50"
antont
  • 2,676
  • 1
  • 19
  • 21
  • 3
    Small correction: `t-z` in the original answer does work because [`Date.prototype.valueOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/valueOf) is equivalent to `getTime`, so anything implicitly using that method, like for subtraction and the unary `+` (e.g. `+new Date(2020, 0, 1)`), will use the number of milliseconds. [More info on object to primitive conversion](https://javascript.info/object-toprimitive) – Bali Balo Mar 30 '20 at 10:57
  • ok - am still using that code I think, may try that way at some point – antont Mar 30 '20 at 14:07
  • 2
    This worked like a charm. I did have to change the slice to `(0, 16)` to accommodate ``, which for some reason wants hours and minutes without seconds. – Lori Apr 22 '21 at 22:03
8

AFAIK you can't format dates in javascript (without using external libraries). The best you could do is "format it yourself". I mean:

var date = new Date();
var year = date.getFullYear();
var month = date......


var ISOdate = year + "-" + month + "-" + .... ;

But there are some good libraries that will let you format dates! (read "format" as in library.getDate("YYYY-MM-DD.........");)

EDIT:

Moment.js seems the thing you're looking for: http://momentjs.com/

alexandernst
  • 14,352
  • 22
  • 97
  • 197
  • 1
    and the month returned by `.getMonth()` si not padded, so I guess I would need a library to pad it too - seems like a lot of a faff! – Billy Moon Sep 13 '12 at 19:33
  • Yes, indeed. It's a lot of work, that's why I'd recommend using Moment.js or some other library. – alexandernst Sep 13 '12 at 19:36
7

Although answers here might work, there's nothing like a simple one-liner:

new Date(new Date().getTime() - new Date().getTimezoneOffset() * 60 * 1000).toISOString()

Explanation for those who are interested:

We create a new instance of Date (the outer one).
We give it another instance of Date (in milliseconds) from which we subtract the timezone offset (in milliseconds too, either positive or negative). All of this we format into ISO

Tzahi Leh
  • 2,002
  • 1
  • 15
  • 27
  • It is very close, but I get one day before: `min = $('#min').val()` -> `"2022-01-01"` `newMin = new Date(new Date(min).getTime() - new Date(min).getTimezoneOffset() * 60 * 1000).toISOString()` -> `"2021-12-31T18:00:00.000Z"` Please note I am passing the min var into the Date object – RobertPro Jan 07 '22 at 01:49
  • This is due to your timezone. The string you gave is when in your local time the year has passed (meaning exactly 2022-01-01, 00:00:00). After the calculation I suggest, we extract the timezone from your local time, and get the actual time in places with GMT+0 (Greenwich!). If you live in GMT+6 area (Bangladesh, Bhutan etc..), your result is correct. There it's midnight when in Greenwich it's still 6pm the day before :) – Tzahi Leh Jan 09 '22 at 09:27
  • This retains the "Z" offset designator, which is only correct if the host is set to +0. It's also somewhat inefficient in creating three Date objects where one will do for the sake of "one–liner". Any code can be one line if semi–colons are in the right place. – RobG Feb 25 '22 at 23:36
2

I don't quite understand which date did you need but I think you need

   const ISOLocaleString = d => {
       const pad = n => n < 10 ? '0'+n : n;    
       return d.getFullYear()+'-'
            + pad(d.getMonth()+1)+'-'
            + pad(d.getDate())+'T'
            + pad(d.getHours())+':'
            + pad(d.getMinutes())+':'
            + pad(d.getSeconds())+'Z'
   }

or

    const ISOUTCLocaleString = d => {
        const pad = n => n<10 ? '0'+n : n;    
        return d.getUTCFullYear()+'-'
             + pad(d.getUTCMonth()+1)+'-'
             + pad(d.getUTCDate())+'T'
             + pad(d.getUTCHours())+':'
             + pad(d.getUTCMinutes())+':'
             + pad(d.getUTCSeconds())+'Z'
    }
  • The "Z" indicates offset +0 but the parts are not unless the host is UTC or GMT. You need to also convert the offset to ±HH:mm format and append it instead of Z. – RobG Feb 25 '22 at 23:27
1

Another method is to define a prototype on the Date object that leverages the built-in toISOString method (a separate function is also provided):

// Extend Date.prototype
Date.prototype.toLocalISOString = function() {
  // Get the local offset for the date in minutes
  let offsetMins = this.getTimezoneOffset();
  // Get a time value adjusted for the offset
  let localTimeMs = this - offsetMins * 6e4;
  // Make a new Date so don't affect this
  let date = new Date(localTimeMs);
  // Get the local offset sign (ECMAScript sign is opposite to usual)
  let utcOffsetSign = offsetMins > 0? '-' : '+';
  // Remove sign from offsetMins
  offsetMins = Math.abs(offsetMins);
  // Get offset hours and minutes, padd to 2 digits
  let utcOffsetHr = String(offsetMins / 60 | 0).padStart(2,'0');
  let utcOffsetMin = String(offsetMins % 60).padStart(2,'0');
  // Build offset string
  let utcOffsetString = `${utcOffsetSign}${utcOffsetHr}:${utcOffsetMin}`;
  // Return as adjusted ISO 8601 format string with adjusted offset
  return date.toISOString().replace('Z', utcOffsetString);
};

// E.g.
let date = new Date();
// Like 2020-08-04T14:52:38.613-07:00
console.log(`UTC  : ${date.toISOString()}\n` +
            `Local: ${date.toLocalISOString()}`);
            
// Stand alone function
function toISOLocal(date) {
  let offsetMins = date.getTimezoneOffset();
  let d = new Date(date - offsetMins*6e4);
  let offsetSign = offsetMins > 0? '-' : '+';
  offsetMins = Math.abs(offsetMins);
  let offsetHr = String(offsetMins / 60 | 0).padStart(2,'0');
  let offsetMin = String(offsetMins % 60).padStart(2,'0');
  return d.toISOString().replace('Z', `${offsetSign}${offsetHr}:${offsetMin}`);
}

// Like 2020-08-04T14:52:38.613-07:00
console.log(`fn   : ${toISOLocal(date)}`);
RobG
  • 142,382
  • 31
  • 172
  • 209
de3z1e
  • 411
  • 4
  • 8
1

In the original post, the current ISO date is "2012-09-13T19:12:23.826Z"

If what is wanted is a date that respects ISO norm but reflects the local offset from UTC time, the target answer should be "2012-09-13T22:12:23.826+03:00".

Best answer in the post bellow, with code snippet that respects the ISO 8601 format / RFC 3339

https://stackoverflow.com/a/17415677/1563072

Zitoun
  • 446
  • 3
  • 13
0

ISO 8601 is simply a way of formatting dates and can as such can be applied to any time zone.

Have you tried the timeZone option to the Date object's toLocaleString method?

This question has answers with examples.

Lee Goddard
  • 10,680
  • 4
  • 46
  • 63
  • The problem with *toLocaleString* is that the timezone option does not return consistent results. Sometimes it returns the civil timezone name or abbreviation, other times it returns UTC/GMT±HH:mm depending on various parameters in ways that are not obvious or easily accommodated. – RobG Feb 25 '22 at 23:32
  • @RobG Are you saying it is not easy to use? Or that it has a bug? – Lee Goddard Feb 27 '22 at 12:59
  • I'm saying the results are inconsistent because it attempts to present results that are considered typical for the chosen language. So in the same way it orders day, month and year components depending on the language provided and of the host, it might return say "EST", "GMT-5" or "UTC-5" for the short timezone name. Hence the new methods (not widely implemented) of "shortOffset", "longOffset", "shortGeneric" and "longGeneric" to reduce the unpredictability of the result. Most of this stems from the confusion of timezone with offset (and language with locale, but that's another sore point…). – RobG Feb 27 '22 at 22:55
0

There's no direct way to do this. However, you can use toLocaleString to create a string that you can easily parse to make it an ISO string.

This works on node:

function getLocalIsoTime(time, timezone) {
        const local = time.toLocaleString("en-US", {timeZone: timezone, hour12: false, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit"});
        return `${local.substr(6, 4)}-${local.substr(0, 2)}-${local.substr(3, 2)}T${local.substr(12, 8)}`;
    }

console.log(getLocalIsoTime(new Date(), 'Asia/Kolkata'));

A simpler version works on node 15+ and on most modern browsers:

function getLocalIsoTime(time, timezone) {
        return time.toLocaleString("en-CA", {timeZone: timezone, hour12: false}).replace(/, /, "T");
    }
    
console.log(getLocalIsoTime(new Date(), 'Asia/Kolkata'));
RobG
  • 142,382
  • 31
  • 172
  • 209
roncli
  • 196
  • 2
  • 8
0

This is the method I use. It takes care to add zeros if date/month/hour etc is single digit number (for example turns '6' to '06')

function formatLikeISO (d: Date): string {
    let lengthChecker = (str) => {
        if (str.length == 1) { return '0' + str; }
        return str;
    }

    let theDate = lengthChecker(String(d.getDate()));
    let month = lengthChecker(String(d.getMonth() + 1));
    let mins = lengthChecker(String(d.getMinutes()));
    let hours = lengthChecker(String(d.getHours()));
    let secs = lengthChecker(String(d.getSeconds()));

    let formatted =
        d.getFullYear() + '-' +
        month + '-' +
        theDate + ' ' +
        hours + ':' +
        mins + ':' +
        secs;

    return formatted;
}

This will output this kind of format:

"2021-06-30 11:31:34"

JohnPan
  • 1,185
  • 11
  • 21
0

Based on https://stackoverflow.com/a/68191554/833960, but I wanted the ms to be present and consistent width output of 3 digits for ms.

function formatLikeISO(d: Date): string {
  let lengthChecker = (str: string, targetLen = 2) => {
    if (str.length < targetLen) {
      return str.padStart(targetLen, '0')
    }
    return str
  }

  let day = lengthChecker(String(d.getDate()))
  let month = lengthChecker(String(d.getMonth() + 1))
  let mins = lengthChecker(String(d.getMinutes()))
  let hours = lengthChecker(String(d.getHours()))
  let secs = lengthChecker(String(d.getSeconds()))
  let ms = lengthChecker(String(d.getMilliseconds()), 3)

  let formatted =
    d.getFullYear() +
    '-' +
    month +
    '-' +
    day +
    ' ' +
    hours +
    ':' +
    mins +
    ':' +
    secs +
    '.' +
    ms

  return formatted
}

Output:

2023-02-13 10:23:59.025
2023-02-13 10:23:59.026
2023-02-13 10:23:59.026
2023-02-13 10:23:59.030
2023-02-13 10:23:59.438
2023-02-13 10:23:59.438
MattG
  • 5,589
  • 5
  • 36
  • 52
0

Simplest answer I have found:

new Date().toLocaleString("sv").replace(" ","T")

results in "2023-04-15T08:07:19"

I.e. Sweeden (sv) uses a form of the ISO8601/RFC3339 date format out of the box.

Pylinux
  • 11,278
  • 4
  • 60
  • 67