Published on October 20, 2023

How to Handle Time-Zones in JavaScript with date-fns-tz

How to Handle Time-Zones in JavaScript with date-fns-tz

If you’ve ever had the dubious pleasure of dealing with dates and times across different time-zones in JavaScript, then you’ll appreciate that it can quickly become a headache. When dealing with data from various parts of the world, this headache includes trying to ensure that timestamps and their respective time-zones correlate logically. Even Hollywood movies - to the amusement of programmers everywhere - have shown that messing around with time-zones can lead to all sorts of undesirable effects!

In node.js, we previously relied on external libraries such as moment.js to handle dates. However, as we try to move towards lighter, performant applications, libraries such as moment.js, which are significantly larger in size, may not fit the bill.

Enter date-fns which offers an alternative solution with a set of helper functions for handling date math and time conversions in node.js and the browser. Now, combined with the date-fns-tz extension for time-zone support, we can easily handle time-zone specific tasks with lower overhead than ever before.

Understanding the Basics

Now, before we step into Doctor Who's Tardis and begin our gallivanting through time, let's first lay out some groundwork. date-fns-tz is an extension of date-fns. It makes full use of the Intl API from JavaScript.

Now, the Internationalization API, abbreviated as the Intl object, is a built-in object in JavaScript that provides locale-specific functionality. It has various service constructors, such as those for dates and time formats. This API facilitates operations based on specific languages, regions and various cultural scenarios.

In the context of date and time conversion, the Intl API provides date-time formatting capabilities, including supporting different calendar types, numbering systems, time-zones, and more.

Getting Started with date-fns-tz

To make use of date-fns-tz, you'll first have to install it alongside date-fns which is a peer dependency. You can add them both to your project with the following commands:

npm install date-fns --save
npm install date-fns-tz --save

Once installation is complete, you can import the necessary functions from 'date-fns-tz' as per requirement. This library supports both CommonJS and ESM imports.

Let’s first understand what date-fns-tz allows us to do. With it, we are able to:

  1. Format dates in any time zone.
  2. Define a date-time representing local time in a given time zone from the UTC date.
  3. Convert a known local time in a different time zone to its equivalent UTC time.

A memorable Halloween Party (across Time-Zones)

In order to make our journey more fun and relatable, let's consider a fictional scenario for our examples. In the spirit of Halloween (and since our specific date for examples is 2023 October 20th), let's say you, as the head of global operations at a multinational corporation, are organizing a virtual Halloween party across different spare time-zones simultaneously (a daunting task even without the time-zone issue!).

Now, as part of this party, you want to schedule a global announcement to kick things off. Since all the employees around the world are supposed to join in at the same time, you need to inform them about when the kick-off will happen in their own local time zones.

Formatting Dates in a Timezone

Our first task is to format the date of the party in a specific time zone. date-fns-tz provides us with a function called formatInTimeZone for this.

import { formatInTimeZone } from 'date-fns-tz'

const date = new Date('2023-10-20T22:00:00Z') // The party begins at 10 PM UTC time

// Let's inform our teammate in New York when the party starts
const newYorkTime = formatInTimeZone(date, 'America/New_York', 'yyyy-MM-dd HH:mm:ssXXX')

console.log(`Hey teammate, remember the party starts on: ${newYorkTime}`)

Here, we're organizing a party that starts at 10 PM UTC time on the 20th of October 2023. Using the formatInTimeZone function, we are able to inform our team-mate in New York that the kick-off will be at 6 PM local time (because New York follows the UTC-4:00 offset).

This way, we can clearly communicate to each teammate around the world when the party will start in their own local time, eliminating any time related confusion!

The formatInTimeZone takes a Date instance, along with the IANA time zone name, and the format in which we want the date-time. This function ensures that we get the date-time in the target time zone.

Converting Local Time to UTC

Now, as we continue with our party planning, we encounter a small hitch. Our party decoration specialist requires time to set up before the party starts. But, he is based out of Tokyo, Japan.

If he tells you that he needs 3 hours for decoration and will finish his setup by 7 PM Tokyo time, then you need to understand what time it will be in UTC to correctly schedule the party starting time.

In such cases, date-fns-tz provides us a function called zonedTimeToUtc.

import { zonedTimeToUtc } from 'date-fns-tz'

const localSetupTimeString = "2023-10-20T19:00:00" // The setup will be over by 7 PM Tokyo time

const utcSetupCompleteTime = zonedTimeToUtc(localSetupTimeString, 'Asia/Tokyo')

console.log(`Setup will be complete at ${utcSetupCompleteTime.toISOString()} UTC time`)

Here, we've used this function to convert the local time in Tokyo to the equivalent UTC time. Now we can plan our kick-off time accordingly and allow for proper setup.

Mvc-ing from UTC to Local Time

Now that we know when the setup will be complete, we need to inform another teammate, let's say in Sydney, Australia, when he should start the music (as we all know, no party starts without music!).

The key here is that because he's in a different time-zone, we have to convert the UTC kick-off time to Sydney time. Fortunately, date-fns-tz has a function utcToZonedTime to help us:

import { utcToZonedTime } from 'date-fns-tz'

const utcTimeString = "2023-10-20T22:00:00Z" // The party starts at 10 PM UTC time

const localStartSydneyTime = utcToZonedTime(utcTimeString, 'Australia/Sydney')

console.log(`Hey DJ, please start the music at ${localStartSydneyTime}`)

So, just by calling this function with the UTC time and the desired time-zone (in this case, 'Australia/Sydney'), we can tell our DJ exactly when to start the music according to his local time in Sydney. This prevents any confusion about the start time of the party (and ensures our beats are not late!).

The date-fns-tz library provides more functions in addition to the ones outlined above. Functions like getTimezoneOffset to calculate the difference in milliseconds between two timezones, or toDate which can be used to parse a Date from a string containing a date and time in any time zone.

In the process of orchestrating our fictional multinational Halloween party, we've only begun to scratch the surface of what we can do with date-fns and date-fns-tz. Hopefully, this has shown you that working across time-zones doesn't have to be a frightening experience - even around Halloween!

Interested in LogSnag?

Get started right now for free!