385
votes

I have date time in a particular timezone as a string and I want to convert this to the local time. But, I don't know how to set the timezone in the Date object.

For example, I have Feb 28 2013 7:00 PM ET, then I can

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);  

As far as I know, I can either set the UTC time or local time. But, how do I set time in another timezone?

I tried to use the add/subtract the offset from UTC but I don't know how to counter daylight savings. Am not sure if I am heading the right direction.

How can I go about converting time from a different timezone to local time in javascript?

15
Date objects don't have a timezone, they are UTC.RobG
worth reading this article medium.com/@toastui/…, the conclusion is you can't in JavaScript but you can use libraries like Moment Timezone to do itNeekey
Date objects most certainly DO have timezones. That's why they have two different sets of getters and setters. One for UTC, one for local.John Lord
@JohnLord No, the local getters simply convert the stored UTC time into the host timezone. For example, do d = new Date() then d.getHours() in the console, change your computer's time zone by an hour, and try d.getHours() again. There is no local time zone information stored in d.Lynn

15 Answers

551
votes

Background

JavaScript's Date object tracks time in UTC internally, but typically accepts input and produces output in the local time of the computer it's running on. It has very few facilities for working with time in other time zones.

The internal representation of a Date object is a single number, representing the number of milliseconds that have elapsed since 1970-01-01 00:00:00 UTC, without regard to leap seconds. There is no time zone or string format stored in the Date object itself. When various functions of the Date object are used, the computer's local time zone is applied to the internal representation. If the function produces a string, then the computer's locale information may be taken into consideration to determine how to produce that string. The details vary per function, and some are implementation-specific.

The only operations the Date object can do with non-local time zones are:

  • It can parse a string containing a numeric UTC offset from any time zone. It uses this to adjust the value being parsed, and stores the UTC equivalent. The original local time and offset are not retained in the resulting Date object. For example:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toISOString()  //=> "2020-04-12T16:00:00.000Z"
    d.valueOf()      //=> 1586707200000  (this is what is actually stored in the object)
    
  • In environments that have implemented the ECMASCript Internationalization API (aka "Intl"), a Date object can produce a locale-specific string adjusted to a given time zone identifier. This is accomplished via the timeZone option to toLocaleString and its variations. Most implementations will support IANA time zone identifiers, such as 'America/New_York'. For example:

    var d = new Date("2020-04-13T00:00:00.000+08:00");
    d.toLocaleString('en-US', { timeZone: 'America/New_York' })
    //=> "4/12/2020, 12:00:00 PM"
    // (midnight in China on Apring 13th is noon in New York on April 12th)
    

    Most modern environments support the full set of IANA time zone identifiers (see the compatibility table here). However, keep in mind that the only identifier required to be supported by Intl is 'UTC', thus you should check carefully if you need to support older browsers or atypical environments (for example, lightweight IoT devices).

Libraries

There are several libraries that can be used to work with time zones. Though they still cannot make the Date object behave any differently, they typically implement the standard IANA timezone database and provide functions for using it in JavaScript. Modern libraries use the time zone data supplied by the Intl API, but older libraries typically have overhead, especially if you are running in a web browser, as the database can get a bit large. Some of these libraries also allow you to selectively reduce the data set, either by which time zones are supported and/or by the range of dates you can work with.

Here are the libraries to consider:

Intl-based Libraries

New development should choose from one of these implementations, which rely on the Intl API for their time zone data:

Non-Intl Libraries

These libraries are maintained, but carry the burden of packaging their own time zone data, which can be quite large.

* While Moment and Moment-Timezone were previously recommended, the Moment team now prefers users chose Luxon for new development.

Discontinued Libraries

These libraries have been officially discontinued and should no longer be used.

Future Proposals

The TC39 Temporal Proposal aims to provide a new set of standard objects for working with dates and times in the JavaScript language itself. This will include support for a time zone aware object.

138
votes

As Matt Johnson said

If you can limit your usage to modern web browsers, you can now do the following without any special libraries:

new Date().toLocaleString("en-US", {timeZone: "America/New_York"})

This isn't a comprehensive solution, but it works for many scenarios that require only output conversion (from UTC or local time to a specific time zone, but not the other direction).

So although the browser can not read IANA timezones when creating a date, or has any methods to change the timezones on an existing Date object, there seems to be a hack around it:

function changeTimezone(date, ianatz) {

  // suppose the date is 12:00 UTC
  var invdate = new Date(date.toLocaleString('en-US', {
    timeZone: ianatz
  }));

  // then invdate will be 07:00 in Toronto
  // and the diff is 5 hours
  var diff = date.getTime() - invdate.getTime();

  // so 12:00 in Toronto is 17:00 UTC
  return new Date(date.getTime() - diff); // needs to substract

}

// E.g.
var here = new Date();
var there = changeTimezone(here, "America/Toronto");

console.log(`Here: ${here.toString()}\nToronto: ${there.toString()}`);
39
votes

You can specify a time zone offset on new Date(), for example:

new Date('Feb 28 2013 19:00:00 EST')

or

new Date('Feb 28 2013 19:00:00 GMT-0500')

Since Date store UTC time ( i.e. getTime returns in UTC ), javascript will them convert the time into UTC, and when you call things like toString javascript will convert the UTC time into browser's local timezone and return the string in local timezone, i.e. If I'm using UTC+8:

> new Date('Feb 28 2013 19:00:00 GMT-0500').toString()
< "Fri Mar 01 2013 08:00:00 GMT+0800 (CST)"

Also you can use normal getHours/Minute/Second method:

> new Date('Feb 28 2013 19:00:00 GMT-0500').getHours()
< 8

( This 8 means after the time is converted into my local time - UTC+8, the hours number is 8. )

33
votes

This should solve your problem, please feel free to offer fixes. This method will account also for daylight saving time for the given date.

dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
  let date = new Date(Date.UTC(year, month, day, hour, minute, second));

  let utcDate = new Date(date.toLocaleString('en-US', { timeZone: "UTC" }));
  let tzDate = new Date(date.toLocaleString('en-US', { timeZone: timeZone }));
  let offset = utcDate.getTime() - tzDate.getTime();

  date.setTime( date.getTime() + offset );

  return date;
};

How to use with timezone and local time:

dateWithTimeZone("America/Los_Angeles",2019,8,8,0,0,0)
9
votes

I found the most supported way to do this, without worrying about a third party library, was by using getTimezoneOffset to calculate the appropriate timestamp, or update the time then use the normal methods to get the necessary date and time.

var mydate = new Date();
mydate.setFullYear(2013);
mydate.setMonth(02);
mydate.setDate(28);
mydate.setHours(7);
mydate.setMinutes(00);

// ET timezone offset in hours.
var timezone = -5;
// Timezone offset in minutes + the desired offset in minutes, converted to ms.
// This offset should be the same for ALL date calculations, so you should only need to calculate it once.
var offset = (mydate.getTimezoneOffset() + (timezone * 60)) * 60 * 1000;

// Use the timestamp and offset as necessary to calculate min/sec etc, i.e. for countdowns.
var timestamp = mydate.getTime() + offset,
    seconds = Math.floor(timestamp / 1000) % 60,
    minutes = Math.floor(timestamp / 1000 / 60) % 60,
    hours   = Math.floor(timestamp / 1000 / 60 / 60);

// Or update the timestamp to reflect the timezone offset.
mydate.setTime(mydate.getTime() + offset);
// Then Output dates and times using the normal methods.
var date = mydate.getDate(),
    hour = mydate.getHours();

EDIT

I was previously using UTC methods when performing the date transformations, which was incorrect. With adding the offset to the time, using the local get functions will return the desired results.

4
votes

For Ionic users, I had hell with this because .toISOString() has to be used with the html template.

This will grab the current date, but of course can be added to previous answers for a selected date.

I got it fixed using this:

date = new Date();
public currentDate: any = new Date(this.date.getTime() - this.date.getTimezoneOffset()*60000).toISOString();

The *60000 is indicating the UTC -6 which is CST so whatever TimeZone is needed, the number and difference can be changed.

3
votes

I ran into a similar problem with unit tests (specifically in jest when the unit tests run locally to create the snapshots and then the CI server runs in (potentially) a different timezone causing the snapshot comparison to fail). I mocked our Date and some of the supporting methods like so:

describe('...', () => {
  let originalDate;

  beforeEach(() => {
    originalDate = Date;
    Date = jest.fn(
      (d) => {
        let newD;
        if (d) {
          newD = (new originalDate(d));
        } else {
          newD = (new originalDate('2017-05-29T10:00:00z'));
        }
        newD.toLocaleString = () => {
          return (new originalDate(newD.valueOf())).toLocaleString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleDateString = () => {
          return (new originalDate(newD.valueOf())).toLocaleDateString("en-US", {timeZone: "America/New_York"});
        };
        newD.toLocaleTimeString = () => {
          return (new originalDate(newD.valueOf())).toLocaleTimeString("en-US", {timeZone: "America/New_York"});
        };
        return newD;
      }
    );
    Date.now = () => { return (Date()); };
  });

  afterEach(() => {
    Date = originalDate;
  });

});
3
votes

I had the same problem but we can use the time zone we want
we use .toLocaleDateString()

eg:

var day=new Date();
const options= {day:'numeric', month:'long', year:"numeric", timeZone:"Asia/Kolkata"};
const today=day.toLocaleDateString("en-IN", options);
console.log(today);
2
votes

Try using ctoc from npm. https://www.npmjs.com/package/ctoc_timezone

It has got simple functionality to change timezones (most timezones around 400) and all custom formats u want it to display.

2
votes

Building on the answers above, I am using this native one liner to convert the long timezone string to the three letter string:

var longTz = 'America/Los_Angeles';
var shortTz = new Date().
    toLocaleString("en", {timeZoneName: "short", timeZone: longTz}).
    split(' ').
    pop();

This will give PDT or PST depending on the date provided. In my particular use case, developing on Salesforce (Aura/Lightning), we are able to get the user timezone in the long format from the backend.

1
votes

Try: date-from-timezone, it resolves expected date with help of natively available Intl.DateTimeFormat.

I used that method in one of my projects for few years already, but it's now I decided to publish it as small OS project :)

1
votes

I know its 3 years too late, but maybe it can help someone else because I haven't found anything like that except for the moment-timezone library, which is not exactly the same as what he's asking for here.

I've done something similar for german timezone, this is a little complex because of daylight saving time and leap years where you have 366 days.

it might need a little work with the "isDaylightSavingTimeInGermany" function while different timezones change on different times the daylight saving time.

anyway, check out this page: https://github.com/zerkotin/german-timezone-converter/wiki

the main methods are: convertLocalDateToGermanTimezone convertGermanDateToLocalTimezone

I've put an effort into documenting it, so it won't be so confusing.

0
votes
//For mumbai time diffrence is 5.5 hrs so
// city_time_diff=5.5 (change according to your city) 

let timee= Date.now()
    timee=timee+(3600000*city_time_diff); //Add our city time (in msec) 
    let new_date=new Date(timee)
console.log("My city time is: ",new_date);
-2
votes

Maybe this will help you

/**
 * Shift any Date timezone.
 * @param {Date} date - Date to update.
 * @param {string} timezone - Timezone as `-03:00`.
 */
function timezoneShifter(date, timezone) {
  let isBehindGTM = false;
  if (timezone.startsWith("-")) {
    timezone = timezone.substr(1);
    isBehindGTM = true;
  }

  const [hDiff, mDiff] = timezone.split(":").map((t) => parseInt(t));
  const diff = hDiff * 60 + mDiff * (isBehindGTM ? 1 : -1);
  const currentDiff = new Date().getTimezoneOffset();

  return new Date(date.valueOf() + (currentDiff - diff) * 60 * 1000);
}



const _now = new Date()
console.log(
  [
    "Here: " + _now.toLocaleString(),
    "Greenwich: " + timezoneShifter(_now, "00:00").toLocaleString(),
    "New York: " + timezoneShifter(_now, "-04:00").toLocaleString(),
    "Tokyo: " + timezoneShifter(_now, "+09:00").toLocaleString(),
    "Buenos Aires: " + timezoneShifter(_now, "-03:00").toLocaleString(),
  ].join('\n')
);
-3
votes

Was facing the same issue, used this one

Console.log(Date.parse("Jun 13, 2018 10:50:39 GMT+1"));

It will return milliseconds to which u can check have +100 timzone intialize British time Hope it helps!!