Set Timezone by Name in JavaScript Date

Here's how to set a timezone by name on a JavaScript Date object. It can be tricky.

The goal

What we're trying to do is to create the Date in a named IANA timezone (eg, 'America/New_York') other than that of the runtime environment.

Setting the Timezone

The tl;dr on successfully setting the timezone is:

  • Set it when the Date is instantiated, don't adjust it.

  • There is no one-line native-JavaScript API available to set the Timezone by name at the time of creation.

  • You can natively set the timezone by using offsets.

Re adjustments: Setting the timezone after the fact will adjust the time when you don't mean it to. Asking for the output to be shown in a certain timezone will result in the same thing. It's like saying, "I have a date at a certain time, but I want to see it at another time, specifically this time zone."

Re native API: To do this natively, you'd have to do know the timezone offset and specify it in an ISO string when instantiating the native Date object.

A 3rd-party library is required to set the timezone by name (eg, "America/New_York"). I chose to use luxon, the successor to moment.js.

The proper luxon API to create a Date in a timezone is:

DateTime.local(..., { zone: 'America/New_York' })

The key is the final options parameter, with the zone key, set to an IANA timezone.

See this article for a lot of other context on JS Dates and timezones.

Different APIs and their outputs

Let's create a Date in another timezone. I'm in MT in this output. I'm trying to create the Date to be 7:30pm ET (or 19:30 ET).

Can't use setZone after the fact. Adjusts from -0:00 offset to -4:00.

> DateTime.utc(2023, 4, 7, 19, 30, 0).setZone('America/New_York').toString()
'2023-04-07T15:30:00.000-04:00'

This time, using a .local (ie, not UTC) Date, the outcome of moving hours when I don't mean to is the same. The offset to start is different, that's all:

> DateTime.local(2023, 4, 7, 19, 30, 0).setZone('America/New_York').toString()
'2023-04-07T21:30:00.000-04:00'

Back to native APIs. The date created is supposedly Eastern time (-4:00). But pulling it out with .toLocaleString adjusts it as if the input was Mountain time, like above:

> new Date('2023-04-07T21:30:00.000-04:00').toLocaleString('en-US', { timeZone: 'America/New_York' })
'4/7/2023, 9:30:00 PM'

If I don't specify the output timezone, I just get whatever's set (here MT) or not (in the case of UTC):

> new Date("2020-04-07T19:30:00.000+08:00").toString()
'Sat Apr 7 2020 05:30:00 GMT-0600 (Mountain Daylight Time)'

So, back to the API from luxon that works:

> DateTime.local(2023, 4, 7, 19, 30, 0, { zone: 'America/New_York' }).toString()
'2023-04-07T19:30:00.000-04:00'

Voila!

Now, this works. But using luxon is not my favorite. Does anyone know a secret native API that does this? (At some point luxon goes native, right.) What have I missed here?