DEV Community

Cover image for Removing Timezones from Dates in Javascript
Shubham Patil
Shubham Patil

Posted on

Removing Timezones from Dates in Javascript

Foreword

It's been a while since I've wrote on here. I'm planning to write smaller pieces on here that relate to problems that I solve on a day-to-day basis.

Background

I was working on the React Native app the other day for my travel-planning startup, Tripley, and came across a somewhat unique problem. When creating events for the trip calendar, it would store dates with the current timezone in mind.

A Bit (Too Much) About Timezones

map of timezones

To prevent one side of the world eating their lunches in darkness, timezones were created. PST (Pacific Standard Time) is 3 hours behind EST (Eastern Standard Time). This means that 1:00 in San Francisco is equivalent to 4:00 in New York.

Each timezone gets a code relative to the timezone in London, UK. The timezone in London is GMT (Greenwich Mean Time). Why London? Imperialism.

"Universal Coordinated Time" (UTC) is derived from GMT (it's essentially the same time). The main difference between UTC and London's time is that UTC doesn't care about daylight savings. When The UK undergoes daylight savings and switches to the BST (British Summer Time) timezone, UTC stays the same time as it was before.

Each timezone gets a code relative to the timezone in London, UK.

Back to this. Each timezone gets a time in hours relative to the UTC time. PST's "code" is UTC-8, which specifies that PST is 8 hours behind UTC time. This isn't limited to just hours. Nepal, for example, has a code of UTC+05:45, which specifies that it is 5 hours and 45 minutes ahead of UTC time. BST even has a UTC code, which is UTC+1. In daylight savings time for Britain, the BST timezone is one hour of UTC time.

The Problem

This was a big problem for us. Say that you selected August 8th, 2023 at 4:30 PM while sitting in San Francisco. By the nature of JavaScript, the date would be registered under UTC time (in this case, it would be August 8th, 2023 at 11:30 PM UTC). For your trip, if you travel to the island of Oahu, Hawaii, you would be under the HST timezone (Hawaiian Standard Time), which is 3 hours behind PST (Pacific Standard Time).

The event that you scheduled for 4:30 PM would now show up at 1:30 PM for you. This would occur because JavaScript automatically adjusts UTC dates for you based on your current timezone. It would convert the UTC date we had to the HST timezone.

Suppose we had a JS date variable called date, and we ran this in San Francisco:

//Wed Feb 22 2023 14:00:43 GMT-0800 (Pacific Standard Time)
console.log(date);
Enter fullscreen mode Exit fullscreen mode

Now, if we ran this in Honolulu, it would look a bit different:

//Wed Feb 22 2023 11:00:43 GMT-1000 (Hawaiian Aleutian Time)
console.log(date);
Enter fullscreen mode Exit fullscreen mode

In either timezone, if you print the date using the .toISOString() method, you will get a date in the ISO 8061 format:

//2023-02-22T22:00:43.000Z
console.log(date.toISOString())
Enter fullscreen mode Exit fullscreen mode

This ISO string is in UTC time. If we dissect this string, we can see that the T is a separator in the string. So, we have two portions: 2023-02-22 and 22:00:43.000Z. The first part is easily identifiable; it's the date! The second part is the time string.

Our original date in PST was 14:00:43, but this date reads 22:00:43? This is because the date is stored in UTC. The PST timezone is 8 hours behind UTC right? If you add 8 hours to 14:00:43, you get 22:00:43!

The Solution

I tried a couple things when I learned about this. First of all, I tried to see if I could omit the timezone. My first thought was to use the .toLocaleString() method.

Here is how it would look like:

const date = new Date();

// "2/23/2023, 1:48:30 PM"
console.log(date.toLocaleString())
Enter fullscreen mode Exit fullscreen mode

I decided that I would put this string (2/23/2023, 1:48:30 PM) in my database. I thought that I could then input this as a parameter to the Date constructor on the client, like so:

new Date("2/23/2023, 1:48:30 PM")
Enter fullscreen mode Exit fullscreen mode

I tried the code above in the MDN Web Docs JS Date Playground with my computer set in PST and HST:


Code run in PST timezone


Code run in HST timezone

I looked at both results and they were the same! I finally thought that this would be a great solution: store the output of .toLocaleString() and convert it to a Date again on the client, all without the timezone.

However, when I implemented the solution in my React Native app, it didn't work. It kept telling me that I had passed an invalid date into the Date constructor. I searched on the internet. Then, I found this on the MDN Web Docs:

dialog box on docs saying Note: Parsing of strings with Date.parse is strongly discouraged due to browser differences and inconsistencies.

This gave me a hint as to why the code worked in my browser but not my React Native app. The JS in Firefox was running on a different runtime than React Native.

Short Note About JS Engines

Since JavaScript is so uncoordinated, JS code typically runs in a specific environment called an engine. Each browser has their own engine: Chrome uses the V8 engine, Firefox uses their SpiderMonkey engine, and Safari uses JavaScriptCore.

In React Native's case, it uses an engine called Hermes:
hermes logo

I was testing my code on Firefox, which uses the SpiderMonkey engine, and running my code in my React Native app, which uses Hermes. Within these two engines, there must be a discrepancy between how the Date constructor parses a given string.

Second Round of Solutions

Now (after 2 hours) that I realized that my first idea wouldn't work, I came up with another

If we create a Date object with the given ISO string, the UTC time automatically gets converted to the current timezone.

const date = new Date('2023-02-22T22:00:43.000Z')

// Wed Feb 22 2023 14:00:43 GMT-0800 (Pacific Standard Time)
console.log(date)
Enter fullscreen mode Exit fullscreen mode

Now, if we remove the Z from the end of the ISO8601 string, the timezone does not get automatically converted.

// notice the change
const date = new Date('2023-02-22T22:00:43.000')

// Wed Feb 22 2023 22:00:43 GMT-0800 (Pacific Standard Time)
console.log(date)
Enter fullscreen mode Exit fullscreen mode

The console.log output changes from 2:00 PM to 10:00 PM

Pseudocode?

So here was the idea that I had. For simplicity purposes, let's select a date & time from the datepicker in our app: Feb 22 2023 at 4:30 PM

If we console.log this Date object's ISO8061 string, we'll get:

2023-02-23T00:30:00.000Z
Enter fullscreen mode Exit fullscreen mode

Notice how the date is one day ahead and the time is 8 hours ahead of our given time. We can offset this difference simply by subtracting 8 hours (since I created this in the PST timezone) from the UTC time.

If we do that (subtract 8 hours), we get

2023-02-22T16:30:00.000Z
Enter fullscreen mode Exit fullscreen mode

However, if we create a new Date object and console.log that, we get this string:

Wed Feb 22 2023 22:00:43 GMT-0800 (Pacific Standard Time)
Enter fullscreen mode Exit fullscreen mode

We can still see that the UTC time is being converted to the local time. We can fix that by chopping off that trailing Z character in the ISO8601 string.

This is what the resulting ISO8601 string would look like:

2023-02-22T16:30:00.000
Enter fullscreen mode Exit fullscreen mode

Now, if we console.log the given Date object in San Francisco, we get this string:

Wed Feb 22 2023 16:30:00 GMT-0800 (Pacific Standard Time)
Enter fullscreen mode Exit fullscreen mode

Now, despite the timezone information still being there in the console.log, if we run the same code in Honolulu, we'll get

Wed Feb 22 2023 16:30:00 GMT-1000 (Hawaiian Aleutian Time)
Enter fullscreen mode Exit fullscreen mode

The two dates line up! Now here is the actual implementation for the pseudocode:

export const dateWithoutTimezone = (date: Date) => {
  const tzoffset = date.getTimezoneOffset() * 60000; //offset in milliseconds
  const withoutTimezone = new Date(date.valueOf() - tzoffset)
    .toISOString()
    .slice(0, -1);
  return withoutTimezone;
};
Enter fullscreen mode Exit fullscreen mode

First tzoffset initializes a new Date and get the timezone offset in milliseconds (in my case, it would be 28800000 milliseconds).

Then, we subtract that from the value of date, which was 10:30 PM, so it would now be 4:30 PM. Then, we cut off the trailing Z from the ISO String so that JS doesn't consider it in UTC and convert it.

And there we go! The function returns an ISO String that we can now store in our databases.

To parse this date again, you can simply run:

new Date("2023-02-22T16:30:00.000")
Enter fullscreen mode Exit fullscreen mode

and if we console.log that, we'll get

Wed Feb 22 2023 16:30:00 GMT-1000 (Hawaiian Aleutian Time)
Enter fullscreen mode Exit fullscreen mode

End

Hey thanks for reading my blog! I'm planning on writing small blogs (if schoolwork doesn't get in the way).

It's been a while since I last blogged, but in that time I've founded Tripley, an app that helps you plan and go on trips much more easily. It would help us out a ton if you could sign up for our waitlist and give us a follow on Twitter and Instagram. Thanks!

If you want to see more of my daily thoughts, follow my Twitter, much appreciated!

Top comments (6)

Collapse
 
oskarhertzman profile image
Oskar Hertzman

Thanks, great article.

Collapse
 
sonicx180 profile image
sonicx180

awesome!

Collapse
 
darkterminal profile image
Imam Ali Mustofa

For me this is clean and mind blowing solution instead using other library. Damn bruh!!! You got πŸ”₯in your 🀯

Collapse
 
polypixeldev profile image
polypixeldev

This is really useful, thanks!

Collapse
 
ryanauj profile image
Ryan Jackson

Thank you for the write up on this! Very helpful resource for a frustrating problem!

Collapse
 
joshuahavilandcurtis profile image
Josh

Awesome article mate! I'm planning to write pieces on here that relate to problems that I solve on a day-to-day basis.