Be Aware of Browsers’ Internationalization API

Jon Abrams
3 min readMar 29, 2016

--

Browser makers have been giving us web developers lots of great new tools lately. Whether it’s variables in CSS or new JavaScript language features, it can be hard to keep up. One lesser known new browser feature is known as the ECMAScript Internationalization API. It exposes info to JavaScript like a user’s locale, 24hr vs 12hr time format, time zone, and more. Accessing the user’s time zone directly sounds great… right?

This is a story of how it caused a nasty bug.

I work at Hired as a software engineer. Hired is an app that helps people get jobs. One part of our app involves scheduling interviews between employers and candidates. When an employer schedules an interview, we notify the candidate along with the time and place of the interview. But in order to tell them the time of the interview in the email, we need to know the candidate’s time zone.

This was simple at first, when our app was only available in San Francisco, we just assumed everyone was in PST/PDT, but we have since expanded across the country, then into Canada, and now operate in Australia, France, the UK, and even Singapore. The solution? Detect the user’s time zone when they sign up. This worked fine for while, but recently a few users were being assigned the wrong time zone.

Detecting a user’s time zone should be easy but for years it was actually quite difficult. With JavaScript, all you could do was create a new `Date` object and get its offset from UTC, in minutes. For example, if the user was in San Francisco on Christmas, it would report `480`, aka `UTC-8 hours`. But that’s in winter time, if it were summer, the browser would report `420`, since during daylight savings San Francisco is offset from UTC by only 7 hours. So a smart developer knows that for a given user, they should not store in the database just the UTC offset, since it changes depending on the time of the year. The correct solution is to store a string representing the user’s time zone.

Our framework of choice, Ruby on Rails provides a handy list of time zones. Each time zone can be referenced by a string named after the time zone’s biggest city. So if you’re in San Francisco, you’re actually in the “America/Los_Angeles” time zone. If you live in Montreal, eating superior bagels, Rails considers your time zone to be “America/New_York”. The easiest way to avoid any problems is to list out the time zones in a dropdown and have the user find their time zone and store in the db the string representing what they chose. Unfortunately, there are hundreds to choose from! It’s a terrible user experience to ask the user to find their time zone in such a list.

Luckily, there’s a great JS library called jstz. It figures out the user’s time zone string (e.g. “America/New_York”), by asking the browser for date objects at 2014–01–02 and 2014–06–02. It gets the offsets at each date, takes the difference, then looks it up in a table.

jstz is very clever and worked quite well for us. On our signup form we created a hidden time zone dropdown, and let jstz pick the value for us. This worked well for a while, until we started to get reports of people in New York being assigned “America/Los_Angeles” as their time zone.

For some reason, jstz started to return time zones not in Rails’ list of known time zones. jstz was reporting time zones like “America/Montreal”. This was odd, that string doesn’t even exist in the jstz library!

jstz has a feature that detects if the user’s browser supports the new `Intl` object, and if so, jstz uses it to directly request the computer’s time zone, bypassing jstz’s internal list of known time zones. Apparently, your OS knows of a lot more time zones than jstz or Rails does. If a user sets their computer’s time zone to “America/Montreal” or “America/Toronto”, our JS code would try to set our hidden drop down to that value, fail, and default to the first choice: “America/Los_Angeles”. This is because Rails’ time zone dropdown only has “America/New_York” to represent that time zone.

What’s the solution here? Switch to a forked version of jstz without Intl? Perhaps, but if we do that, we lose out on updates to the ever changing time zone rules.

What we ended up doing was temporarily removing Intl before calling jstz:

This forces jstz to use the old, but reliable, technique of checking time zone offsets, and looking up a string in its (relatively) small list of known time zones.

--

--