Specifying a Timezone on the Client


Here's how to specify a timezone on the client, regardless of their browser location.

Why?

Why would I want to do this? In my case, I am writing a tool where a user enters time and date for when the business should do certain things. The business operates in Mountain Timezone, so all its times and dates are to specified as such within the tool.

The problem

The problem comes when the user is working in a browser that's not in Mountain Time. If the browser is in Mountain Time, then all the JavaScript Date()s that are instantiated are in Mountain Time by default.

But what if the user's browser is in Eastern Time? Then, all its instantiated Date()s are in Eastern Time.

Try it in the console. I'm writing this at 10:43am MDT (UTC-6). The Date when the browser is in Eastern Time is 12:43pm EDT (UTC-4), two hours earlier:

new Date()
Thu Sep 18 2025 12:43:24 GMT-0400 (Eastern Daylight Time)

If I was to run new Date().toLocaleDateString() on this, I'd be showing Eastern Time, and that's not what I want.

Emulate

So how does a developer, whose browser is in Mountain Time, reveal the problem of an in-Eastern Time browser and troubleshoot this problem?

Time travel, VPN or Chrome dev tools. Let's choose the easiest.

Open dev tools, top-right 3 dot button, scroll down to Sensors, and a drawer will open.

The first section is called "Location". Change the dropdown to "Other...", then enter an IANA timezone ID. I'll type "America/New_York" for Eastern time.

One moment in time

Thinking about time and timezones can be a challenge. Let's consider these three ISO-8601 timestamps:

2025-09-18T10:43:24-06:00    // MDT
2025-09-18T12:43:24-04:00    // EDT
2025-09-18T16:43:24Z         // UTC

Each of them specifies the exact same moment in time. They are different only in their representation. The context is which these times and dates are shown will matter to the user because of the user's own needs and subsequent interpretation.

Give me one moment in time!

Date input

In this tool, the times and dates are selected using the native datetime-local input, like:

<input type="datetime-local" name="mydate" />

Underneath the input, there's help text that reminds the user that all dates are in Mountain Time. That is the goal.

So, how does one take a timestamp that represents one moment in time, no matter the representation, and show it in Mountain Time? Any representation can come in, but only Mountain Time representation must come out. Thus, we need to set the timezone on the timestamp.

As far as I know, native JavaScript can't do this (yet?). Please correct me if I'm wrong.

Instead, we turn to userland libraries. My favorite is luxon, the successor to moment.js. Just use it for timezone setting, and free yourself.

The value of a datetime-local element uses an ISO-8601-compatible string that does not include seconds or smaller units. Like this:

<input value="2025-08-18T10:43" type="datetime-local" name="mydate" />

Now let's write some code that can format a string like that only in Mountain Time:

import { DateTime } from 'luxon'

const MOUNTAIN_TIMEZONE = 'America/Boise'

function formatDatetimeLocalValueAsMountainTime(dateOrStr?: Date | string | null) {
  if (!dateOrStr) return ''

  const dateTime = typeof dateOrStr === 'string' 
    ? DateTime.fromISO(dateOrStr) 
    : DateTime.fromJSDate(dateOrStr)

  return dateTime.setZone(MOUNTAIN_TIMEZONE).toFormat("yyyy-MM-dd'T'hh:mm")
}

Luxon's DateTime is doing the heavy lifting. We detect input types, then instantiate a DateTime (not a native Date!). We can setZone on that to set a timezone. Then we go straight to DateTime.toFormat. Do not go to Date in between, possibly to use Date.toLocaleDateString instead. It will change your intention of always-Mountain Time to whatever the browser's Date timezone representation is.

Date in transit

Once we have input a date and time as Mountain Time, how do we read the value of the input element and then use that time and date throughout our application. If at all possible, keep dates as UTC as often as possible. So, we'll convert the Mountain Time UI string directly into UTC when reading it. That might look like this:

// called on form submit
function mapDateFromSubmission(formData: FormData) {
  const date = (formData.get('mydate') as string | undefined) ?? undefined
  return date?.length > 0 ? formatUtcFromMountainTime(date) : null,
}

function formatUtcFromMountainTime(mountainTimeDateStr: string): string {
  const date = DateTime.fromISO(mountainTimeDateStr, { zone: MOUNTAIN_TIMEZONE })
  if (!date.isValid) {
    throw new Error('Invalid date string')
  }

  return date.toUTC().toISO({ suppressMilliseconds: true })
}

Again, we're using luxon's DateTime. It can interpret an ISO without timezone data (such as our datetime-local value of 2025-08-18T10:43) in the context of a timezone, hence the zone option passed to DateTime.fromISO. We convert straight to UTC, and then out the door we go. We can pass this around like comfortable UTC anywhere in our app.

Date to display

Now we have UTC datetime strings floating around. What about when we want to display the date? We want Mountain Time only in our app. The answer is the same as it was in the input. We have to create a date, set the timezone as Mountain Time using luxon, and display it as Mountain Time, possibly with a different format. Another formatting function could look like this:

export function formatShortMountainTimeDateTime(date: Date): string {
  return DateTime.fromJSDate(date).setZone(MOUNTAIN_TIMEZONE).toFormat('d MMM yyyy, h:mm a')
}

Again, don't use Date.toLocaleString directly, or you won't show the Mountain Time to users that have browsers outside that timezone.

Bonus: DST

What about Daylight Savings Time? Does it come into play? Yes, and it's handled.

On input, if the user specifies a Mountain Time date on the other side of DST (eg, right now it's on, MDT, but it's for a later date, say in December, when it's off, MST), the user can enter the date without worry about DST. When the input is converted to UTC, the conversion will take care of DST automatically. Then, MST will be UTC-7 instead fo UTC-6 as in MDT. The UTC timestamp will reflect that. Then, you have UTC timestamps floating around. You're good. When you set the timezone to Mountain Time in order to display, the timestamp has the correct offset. You will be shown the correct Mountain Time in the reverse conversion.

So, what did we learn? Again, timezones are tricky. JS Date is not smart enough to do timezone conversions. You need a library like luxon. Convert and format with this library directly. Translation into Date first will mess with the representation you intend. Use UTC around the house in all other cases.