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
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);
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);
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())
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())
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")
I tried the code above in the MDN Web Docs JS Date Playground with my computer set in PST and HST:
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:
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:
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)
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)
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:
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:
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, 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)
Thanks, great article.
awesome!
For me this is clean and mind blowing solution instead using other library. Damn bruh!!! You got 🔥in your 🤯
This is really useful, thanks!
Thank you for the write up on this! Very helpful resource for a frustrating problem!
Awesome article mate! I'm planning to write pieces on here that relate to problems that I solve on a day-to-day basis.