Dealing with Time Zones in Handshake

Posted Sep 21, 2015 by Jordon Dornbos

At Handshake it’s very important for us to display dates and times correctly to our users. Users rely on us every day to be on time for events, career fairs, and interviews and it would be unacceptable to display an incorrect time. On the surface time zones seem to be fairly straightforward to deal with, but as we’ve learned, there are a few pitfalls to look out for.

Timezones

Very early on in Handshake, we made sure to wrap each request in the current user’s time zone using an around_action and Time.use_zone. The users’ time zones were manually set in their profile settings, defaulting based off the time zone of their school. With this in place we would store all dates and times in our database in UTC ±00:00 (because the Time.use_zone would automatically convert this correctly) and then use the %Z format directive to append their time zone abbreviation. This worked pretty well and was easy to get right, but it came with a few downsides. One of the downsides is that the event might not be in your current time zone. If you signed up for an event at a school in Michigan that starts at 9PM, it would say the event starts at 6PM PDT if you were in California. This put a mental strain on users because they had to remember where each event would be at and then translate the time to that time zone. This most likely would not affect students, since all of their times would likely be in their current time zone, but it would be difficult for traveling recruiters. This also wasn’t ideal because users had to manually change their time zone in their profile whenever they geographically changed time zones, which isn’t a great user experience.

To improve these situations we changed three things about how we handled dates and times in Handshake. The first thing we did is add a database column to store each event’s time zone. This way, an object’s time zone is independent of the current user and it can be stored on the event separately from the date and time itself. We also modified our system to automatically update a user’s time zone whenever we detect a change in the browser. Automatically changing the user’s time zone helps ensure that all of the times that we display are accurate given the user’s current location, and ensures the browser’s time zone matches with the user’s server time zone.

In addition to these changes, we’ve also created a time zone library on both the server side and the client side to handle displaying times. This helps bring consistency to our UI and also avoids requiring engineers to opt-into the use of %Z. It also allows us to easily append time zone information when needed and cut it out when it’s not needed. On the server side of things we use the time zone functionality built into Rails with ActiveSupport and on the client side we use Moment.js and Moment Timezone. Putting all of this together resulted in a simple way to display times to the user in the correct format. The thin time zone library we developed always displays the time in the object’s time zone, and then appends a time zone identifier (e.g. EDT) if the current user isn’t in the object’s time zone. While this is all pretty straightforward, there are a few things to look out for.

One of the first things you’ll notice is that some locations don’t observe daylight saving time (e.g. Arizona and Hawaii). Since the time zones we store in our database are Rails time zone names, we need to convert UTC offsets from Moment Timezone to a time zone name that Rails understands. Rails time zone names can be looked up using ActiveSupport::TimeZone[key], where the key is the UTC offset (using the daylight saving time offset if observed). This means that if you’re not careful about checking if a location observes daylight saving time you might end up storing that the user is in a different time zone. A related issue stems from the fact that Rails’ mapping of UTC offsets will return the first alphabetical match. This can result in choosing the wrong time zone name for a given offset. To solve this, you can instead look up a user’s time zone based on their location, rather than use the browser offset.

In summary, Handshake uses Rails’ ActiveSupport paired with Moment.js and Moment Timezone (with a small library on top) to provide a consistent and reliable way of displaying times to our users. We determine the user’s time zone by using their browser’s UTC offset and information about whether or not they’re in a time zone that observes daylight saving time. This has a few pitfalls to watch out for, but works well when done correctly.