Timezone-aware date manipulation for JavaScript in the Browser

Rob Blackbourn
2 min readJun 25, 2022

--

The browser knows about two timezones: the local timezone and UTC. What if you are in New York, and you want to know the time in Tokyo? If you have access to data about the weather in France, but it doesn’t come with the timezone offset? If you have a cross timezone development team?

Under the hood JavaScript knows time as the number of milliseconds since January 1 1970 UTC. There are two sets of methods to get and set the dates: one for the local timezone and one for UTC (e.g. getHours and getUTCHours). Interacting with the Date object is notoriously difficult, and many date libraries exist to make this easier. However, timezones are usually tacked on as an optional extra. The @jetblack/date library takes a timezone-first approach.

npm install --save @jetblack/date

Where timezones are relevant, functions take the timezone as an optional argument (with the default being the local timezone).

import { startOfToday, tzLocal, tzUtc } from '@jetblack/date'// If the timezone isn't specified, it defaults to the local
// timezone.
const todayLocal = startOfToday()
// The following passes it explicitly.
const todayLocalExplicit = startOfToday(tzLocal)
// The start of today relative to UTC can be found by passing the
// UTC timezone.
const todayUTC = startOfToday(tzUtc)
// If the browser had timezone information the following would
// find the start of the day in Tokyo.
const todayTokyo = startOfToday(tzTokyo)

But I’ve already said that the browser doesn’t have access to other timezones! For this we need the sister project @jetblack/tzdata. This is a database of timezones derived from IANA.

npm install --save @jetblack/tzdata

With the timezone database we can make our own timezones.

import { IANATimezone, dataToTimezoneOffset } from '@jetblack/date'
import tokyo from '@jetblack/tzdata/dist/latest/Asia/Tokyo.json'
const tzTokyo = new IANATimezone(
'Asia/Tokyo',
// Convert the dates and durations from JSON strings to objects.
tokyo.map(dataToTimezoneOffset)
)
const todayTokyo = startOfToday(tzTokyo)

This is nice, but what if we don’t know the timezone beforehand? That’s a lot of data to load! Fortunately we can dynamically load the timezone data at runtime.

import {
IANATimezone,
minDataToTimezoneOffset
} from '@jetblack/date'
const timezoneName = 'Asia/Tokyo'
const url = `https://cdn.jsdelivr.net/npm/@jetblack/tzdata/dist/latest/${timezoneName}.min.json`
const response = await fetch(url)
const data = await response.json()
const zoneData = data.map(minDataToTimezoneOffset)
const tzTokyo = new IANATimezone(timeZoneName, zoneData)
const todayTokyo = startOfToday(tzTokyo)

The date library comes with all the usual date arithmetic, including calendars for work day calculations.

--

--