Here’s one approach to letting users select a locale like “America/New_York”:
def locale_list do
now = DateTime.utc_now()
Tzdata.zone_list()
|> Enum.map(fn zone ->
tzinfo = Timex.Timezone.get(zone, now)
offset = Timex.TimezoneInfo.format_offset(tzinfo) # added in v3.78
label = "#{tzinfo.full_name} - #{tzinfo.abbreviation} (#{offset})"
{tzinfo.full_name, label}
end)
|> Enum.uniq()
end
This outputs a list of values like:
[
{"Africa/Abidjan", "Africa/Abidjan - GMT (+00:00:00)"},
...
]
If you wanted to sort these by offset, you could use the value of Timex.Timezone.total_offset(tzinfo)
, but I think alphabetical makes more sense, since the locales are grouped by region.
In the template, I think an <input>
with a <datalist>
is better than a <select>
, as it lets users type and use autocomplete, which in my testing will match any substring, like “africa” or “abidjan” or “gmt” or “05:30” (although exactly how that works is up to the browser). This means that if somebody knows their offset and wants to type it, it can narrow to the locales that match.
<input list="locales" name="user-locale" id="user-locale" value={@user.tz_locale}>
<datalist id="locales">
<%= for {fullname, label} <- @locales do %>
<option value={ fullname } >
<%= label %>
</option>
<% end %>
</datalist>
It’s still not as nice as being able to type the name of whatever town you live in, big or small, but the answers above could help you do that.
Oh yeah, and Intl.DateTimeFormat().resolvedOptions().timeZone
in JS can get the user’s locale from the browser - "DateTimeFormat" | Can I use... Support tables for HTML5, CSS3, etc
So you can add a button to autofill:
<button phx-hook="AutofillLocale">Autofill</button>
…and set up a hook to do it:
Hooks.AutofillLocale = {
mounted() {
this.el.addEventListener("click", e => {
let locale = Intl.DateTimeFormat().resolvedOptions().timeZone
document.getElementById("user-locale").value = locale
})
}
}
Here’s a quick demo:
