Removing Timezones from Dates in Javascript
Tags: javascript, webdev, tutorial, beginners
Published: 2023-04-23 00:43:58


## 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](https://tripley.app), 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ul1iwkrxiotpx07f5dgk.png)

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:
```js
//Wed Feb 22 2023 14:00:43 GMT-0800 (Pacific Standard Time)
console.log(date);
```

Now, if we ran this in Honolulu, it would look a bit different:
```js
//Wed Feb 22 2023 11:00:43 GMT-1000 (Hawaiian Aleutian Time)
console.log(date);
```

In either timezone, if you print the date using the `.toISOString()` method, you will get a date in the ISO 8061 format: 
```js
//2023-02-22T22:00:43.000Z
console.log(date.toISOString())
```

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:
```js
const date = new Date();

// "2/23/2023, 1:48:30 PM"
console.log(date.toLocaleString())
```

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:

```js
new Date("2/23/2023, 1:48:30 PM")
```

I tried the code above in the [MDN Web Docs JS Date Playground](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date) with my computer set in PST and HST:

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2nvr55jhh1ca72h2kmi.png">
  <figcaption>Code run in PST timezone</figcaption>
</figure>


<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gvi20tuzbyaueb5442zz.png">
  <figcaption>Code run in HST timezone</figcaption>
</figure>

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.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f6t31n2xktga3l5qa74n.png)

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](https://hermesengine.dev):
![hermes logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c471vxdtmboph7ctshbf.png)

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.

```js
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)
```

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

```js
// 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)
```

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
```
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
```

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)
```

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
``` 

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)
```
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)
```

The two dates line up! Now here is the actual implementation for the pseudocode:
```ts
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;
};
```

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:
```js
new Date("2023-02-22T16:30:00.000")
```
and if we `console.log` that, we'll get 
```
Wed Feb 22 2023 16:30:00 GMT-1000 (Hawaiian Aleutian Time)
```

---
## 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](https://tripley.app), 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](https://tally.so/r/wQo9aG) and give us a follow on [Twitter](https://twitter.com/tripleyapp) and [Instagram](https://www.instagram.com/tripleyapp/). Thanks!

If you want to see more of my daily thoughts, follow my [Twitter](https://twitter.com/ShubhamPatilsd), much appreciated!