You may have noticed most applications that you interact with and care about your timezone will, at one point, as you for your timezone and normally prepopulate the dropdown with your location's timezone.
However, if you look for this functionality out-of-the-box in Rails, it does not seem to be there (or at least through all my sleuthing for My new app I am building). Though, I have the answer for how you can add this in about ten lines of code through one StimulusJS controller and your neighborhood Rails helpers.
First thing first, you will want to ditch the time_zone_select
helper as the values used in there are not the same as what the browser will use in identifying time zones (I think Rails is not using the standard here, but I didn't dig into too much while solving the technical issue). Instead, we will use a regular select and modify the array sent to a select tag:
<div data-controller="timezone">
<%= f.select :timezone, ActiveSupport::TimeZone.all.collect {|tz| ["(UTC#{ActiveSupport::TimeZone.seconds_to_utc_offset(tz.utc_offset)}) #{tz.name}", tz.tzinfo.name]}, {}, data: {"timezone-target" => "tz"} %>
</div>
When using a select tag, you can provide the second argument an array of strings to represent the "text" and option "value" in the dropdown options. Here "(UTC#{ActiveSupport::TimeZone.seconds_to_utc_offset(tz.utc_offset)}) #{tz.name}"
will construct a string with the offset and Rails timezone name (i.e., (UTC-08:00) Pacific Time (US & Canada)
) and have the options value as the more accepted America/Los_Angeles
. Additionally, at the very end, you can see where we are setting up the StimulusJS controller to interact with our select element, data: {"timezone-target" => "tz"}
, which will produce data-timezone-target="tz"
in the markup. Lastly, this select tag was wrapped in a div
with an attribute to use the Timezone
controller, data-controller="timezone"
.
Next, we can jump to the StimulusJS controller, which will also be simple. It is easiest to use the Rails CLI command to create the controller and update any other files that include controllers into the application.js
:
bin/rails g stimulus timezone
Note - If you do not have Stimulus installed, you can do so with instructions here: https://github.com/hotwired/stimulus-rails.
In the new timezone_controller.js
, you can add the following code:
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ['tz']
initialize() {
this.tzTarget.value = Intl.DateTimeFormat().resolvedOptions().timeZone;
}
}
The Intl.DateTimeFormat().resolvedOptions().timeZone;
is the browser's javascript command for retrieving the timezone. Then, this.tzTarget.value =
sets the dropdown value when the StimulusJS controller initializes.
That's it! About five lines of custom code and another handful of edits to existing Rails tools to pre-select the user's timezone in your Rails application!
This code, as-is, will not work for select boxes that need to be updated as it does not account for not overwriting an existing selected timezone provided by the backend. That'd be as easy as checking for the selected target's value in Stimulus.
Top comments (1)
Or
`require 'geocoder'
require 'tzinfo'
ip = 'Your user IP address here'
location_info = Geocoder.search(ip).first
if location_info && location_info.data['timezone']
timezone = TZInfo::Timezone.get(location_info.data['timezone'])
puts timezone
else
puts "Unable to determine timezone."
end`
:-)