We recently added time zone detection for new users by default in Bullet Train, which I mentioned in passing on Twitter:
In all these years, I don't remember ever implementing auto-detection of a user's time zone at sign-up. Not hard, but it seems like something every app should do, so Bullet Train now does it by default, and provides a view helper for showing timestamps in the user's local time. pic.twitter.com/QrmVPNDHcd— Andrew Culver (@andrewculver) March 27, 2018
A few friends asked how we went about implementing it and it seemed like a blog post would be a good place to share.
Methods for Time Zone Detection
Another method to detect a user's time zone would be to do a geographic look-up on their IP address, but this can produce inaccurate results when a user is browsing through a VPN. This method is also more technically complicated, as it requires having some method to doing IP look-up like a third-party service or database.
Implementing in Rails
We include the jsTimezoneDetect library via Rails Assets:
source 'https://rails-assets.org' do gem 'rails-assets-jsTimezoneDetect' end
//= require jsTimezoneDetect
Detect a User's Time Zone at Registration
We add a hidden field on the registration form like so:
<%= f.hidden_field(:time_zone, value: nil) %>
<script> tag that you could drop right into the form view to keep the example simple.)
Finally, we need to update the registration controller (in our case, we're using Devise,) to assign this field value to the user object:
private def set_time_zone(user) time_zone = params.dig(:user, :time_zone) if time_zone.present? user.time_zone = ActiveSupport::TimeZone::MAPPING.key(time_zone) user.save end end
And then we reference that in the
create method of the registration controller like so:
The secret in the
set_time_zone method is that we're taking the time zone as reported by the jsTimezoneDetect library, (e.g.
America/Los_Angeles,) and converting it to one of the standard
ActiveSupport::TimeZone time zone names (e.g.
Pacific Time (US & Canada).) The latter are more user-friendly because there are way fewer options to choose from.
Allowing Users to Update Their Time Zone
Wherever you allow your users to update their account details, add this field:
<%= form.select :time_zone, time_zone_options_for_select(form.object.time_zone, nil, ActiveSupport::TimeZone) %>
Don't forget to add
:time_zone to your allowed parameters on the controller, or the value won't pass through to the database.
I think that should be enough information to get you started. If you have any questions or feedback about this, please hit me up on Twitter.