Randy Apps


Convenient Apps changing tomorrow.

【JavaScript】Time Zone Calculation #1: Converting Specific City Time to UTC with Standard Features


When handling dates and times in JavaScript, getting the “current time” or the “browser’s local time” is pretty straightforward. However, performing calculations based on a “specific time in a specific time zone” requires a bit more thought.

Why do we even need such calculations? Imagine you are building a world clock app or a meeting scheduler, and you want to implement the following feature:

[The Goal]
“I have a meeting scheduled for January 10, 2026, at 7:00 AM in New York. What time should I be at my computer here in Japan?” We want to solve this programmatically and display the result.

To convert New York time to Japan time, the program essentially needs to go through these three steps:

  1. [Start] Take the user-specified string: “2026-01-10 07:00 AM” for New York.
  2. [Convert to Absolute Time] Convert that string into a Date object in UTC (Coordinated Universal Time), which acts as the global standard.
  3. [Goal] Convert the resulting UTC time back into the local time of your execution environment (Japan) and output, “It is 21:00 (9:00 PM) Japan time.”

This article focuses on Step 2 (reverse-calculating the exact UTC from a specific city’s local time), which is the most challenging part of the process.

I will explain how to do this using only standard JavaScript features (the Date and Intl objects) without relying on external libraries. Along the way, we will also look at the “Daylight Saving Time (DST) trap” that hides within this seemingly perfect approach.

The Limits of the Standard Date Object

First, let’s try creating a Date object simply from a string.

const date = new Date('2026-01-10T07:00:00');

When written like this, which time zone does this actually represent?

The answer is the local time of the environment where the JavaScript is running (the user’s browser). If run in Japan, it’s 7:00 AM Japan Time; if run in New York, it’s 7:00 AM New York Time.

The standard Date object doesn’t have a built-in way to say, “I am running this code in Japan, but please treat this strictly as 7:00 AM in New York.”

To fix this, we need to explicitly add the offset (time difference) to the end of the string. For New York’s standard time (winter), that is -05:00.

// This will correctly be interpreted as "7:00 AM in New York (12:00 PM UTC)"
const date = new Date('2026-01-10T07:00:00-05:00'); 

But how do we dynamically figure out this -05:00 offset? Hardcoding it works if you only care about New York, but that approach isn’t scalable.

If you want to convert times from other global cities, you need to look up their specific offsets. By calculating this offset programmatically, your code will work flawlessly for any time zone.

In short, to reverse-calculate UTC using only standard JavaScript, we need to “dynamically get the correct offset string (like -05:00) for a specific date and time from a given time zone, and append it to the original time string.”

Getting the Offset Using the Intl Object

Here is the code to extract the offset string for a specific city using the standard Intl.DateTimeFormat object.

const targetTimeStr = '2026-01-10T07:00:00';
const targetTimeZone = 'America/New_York';

// 1. Prepare to format the date and time based on the target time zone
const formatter = new Intl.DateTimeFormat('en-US', {
  timeZone: targetTimeZone,
  timeZoneName: 'longOffset' // Specifies formats like "GMT-05:00"
});

// 2. Create a temporary standard Date object (Note: There's a trap here, explained later)
const tempDate = new Date(targetTimeStr);

// 3. Break the date data into individual parts using formatToParts
const parts = formatter.formatToParts(tempDate);

// 4. Extract only the "timeZoneName" part from the array
const offsetPart = parts.find(part => part.type === 'timeZoneName').value;
// Example: offsetPart now holds "GMT-05:00"

// 5. Clean up the string, converting "GMT-05:00" to "-05:00" (or "Z" if it's exactly GMT)
const offsetString = offsetPart.replace('GMT', '') || 'Z';

// 6. Generate the final Date object by combining the original string with our offset
const finalUtcDate = new Date(`${targetTimeStr}${offsetString}`);

console.log(finalUtcDate.toISOString());
// Output: 2026-01-10T12:00:00.000Z

The Intl.DateTimeFormat object is normally used to nicely format dates for display. Here, though, we are using it strictly “as a tool to extract time difference data.” Let’s break down the thought process behind each step.

Step 1: Standardizing the Format

Setting 'en-US' as the first argument is very important. If we set it to 'ja-JP' or leave it blank (relying on the user’s browser), the output format might change depending on their language settings. By forcing the standard English format and adding timeZoneName: 'longOffset', we ensure that the output is always a predictable string like “GMT-05:00” or “GMT+09:00”, no matter where the code runs.

Step 2: Creating a “Seed” for Calculation

To get an offset, we have to pass a Date object into the Intl object so it knows “when” to check the time. Since we don’t know the accurate UTC yet, we create a “temporary Date object” based on our local environment (e.g., Japan) to use as a starting point. (*As it turns out, how we create this “temporary Date” is exactly what causes the bug we’ll discuss later.)

Steps 3 & 4: Safely Extracting Information

If we just called formatter.format(tempDate), we’d get a string like “1/10/2026, GMT-05:00”. But splitting that string by commas or spaces is risky and can lead to bugs.

Instead, we use formatToParts(). This breaks the output into a clean array of objects, like [{type: 'month', value: '1'}, {type: 'timeZoneName', value: 'GMT-05:00'}...]. Using the array’s find method, we can safely grab just the offset string.

Step 5: Formatting for ISO 8601

Our extracted string is “GMT-05:00”. However, if we pass this directly into a new Date(), some browsers will throw an “Invalid Date” error. The standard ISO 8601 format expects just the symbol and numbers, like “-05:00”, without the “GMT”. So, we use replace('GMT', '') to clean it up.

Also, for regions with a zero offset (like London in winter), the result might just be “GMT”, which becomes an empty string after the replace. To handle this, we use the logical OR operator (||) to assign “Z” (Zulu time, meaning UTC) if the string is empty.

Step 6: Generating the Accurate Absolute Time

Finally, we take our cleanly formatted offset string (-05:00) and append it to the user’s original time string (2026-01-10T07:00:00).

When we pass '2026-01-10T07:00:00-05:00' into new Date(), the browser understands: “Ah, this isn’t local time, it’s 7:00 AM in a time zone 5 hours behind UTC.” It then creates an accurate Date object for the global absolute time (12:00 PM UTC).

Goal Achieved: Getting Japan Time

Once we have a perfect UTC Date object (finalUtcDate), the rest is easy. Let’s check our original goal: “What time should I be at my computer here in Japan?”

// Display the time in the local execution environment (Japan)
console.log('Meeting start time in Japan:', finalUtcDate.toLocaleString('ja-JP'));
// Output: Meeting start time in Japan: 2026/1/10 21:00:00

The result is “21:00”! Our code successfully figured out that for a 7:00 AM meeting in New York in January, we need to log on at 9:00 PM Japan time. It seems we’ve successfully reverse-calculated the time.

The “Daylight Saving Time Trap” Hidden Here

While this looks like a solid approach using string manipulation, there’s actually a major flaw in Step 2, where we create that temporary date.

// This line is dangerous
const tempDate = new Date(targetTimeStr);

I ran into this issue while testing the code. I wanted to make sure it worked perfectly for any city, so I tested it using Adelaide, Australia (Australia/Adelaide).

I picked a time right near Adelaide’s Daylight Saving Time (DST) switch, reverse-calculated the UTC using the code above, and converted it back to local time to check it. Surprisingly, the resulting time was off by exactly one hour—it showed “0:30” instead of the correct “1:30”.

Why does this calculation method, which only uses standard JavaScript features (Date and Intl), end up shifting by exactly one hour under certain conditions?

Summary and Next Steps

It is totally possible to reverse-calculate UTC from a specific time zone using standard JavaScript. However, depending on the time difference between your execution environment and the target city, you run the risk of getting the wrong offset.

In the next article, I’ll dive into the Adelaide case study to explain exactly why this DST trap happens (and why it shifts by exactly one hour). I’ll also share how I tried to work around this issue while still sticking strictly to standard JavaScript features.