How to get a User's Timezone into Rails
Tags: rails, ruby, stimulus, javascript
Published: 2023-07-28 07:45:42

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](https://statuscat.com)). 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:

```html
<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`:

```bash
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](https://github.com/hotwired/stimulus-rails).

In the new `timezone_controller.js`, you can add the following code:

```javascript
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.