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.
Very early on in Handshake, we made sure to wrap each request in the current
user’s time zone using an
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.