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?