With Ruby on Rails 6, I like to have times and dates presented in the user’s timezone.
Basecamp’s local_time
My first search results lead me to Basecamp’s local_time
ruby gem for Ruby on Rails. It makes it easy to display times and dates in the local timezone of the user. Ruby on Rails renders times and dates to <time
> elements in UTC timezone. User-agent JavaScript converts those to the local timezone of the user’s web browser.
local_time
is very cache-friendly, as all pages/partials with times and dates contain the same HTML with UTC times and dates.
Unfortunately, I was not skilled enough to add internationalization or localization of the time and date formats to local_time
. Whatever I tried, I ended up with a weird mix of localized and not-localized time/date formats and translations for weekdays and months.
local_time
includes US English internationalization/localization of the JavaScript part in i18n.coffee
. The ruby gem delivers a minimized JavaScript asset for localization. I was not able to enrich it with time/date formats and translations in other languages. Maybe because of all the advancements in the JavaScript ecosystem. I was not able to interpret the internationalization section of local_time
’s documentation ☹
Custom with JavaScript, Cookies, and ApplicationController
Due to my inability to add translations to local_time
’s JavaScript, I dropped local_time
(until I advance to the required level) and did something custom with JavaScript, cookies, and ApplicationController around_action
.
- The web page contains JavaScript to get the local timezone with
Intl.DateTimeFormat().resolvedOptions().timeZone
. The JavaScript code adds the web browser’s timezone to thetimezone
cookie. - On the next request, the browser sends cookie
timezone
to Rails - Rails
ApplicationController
takes the timezone from cookietimezone
and usesTimes#use_zone
inaround_action
to change the timezone for this request - The view renders times and dates in the current timezone
/* app/javascript/user_agent_timezone/index.js */
function setTimezoneCookie() {
var timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
var expires = new Date();
expires.setTime(expires.getTime() + 60*60*24);
expires = expires.toGMTString();
document.cookie = "timezone=" + timezone + "; Path=/";
}
export default setTimezoneCookie()
/* app/javascript/packs/application.js */
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
require("user-agent-timezone")
# app/controlers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :switch_timezone
private
def switch_timezone(&action)
Time.use_zone(timezone_from_cookies, &action)
end
def timezone_from_cookies
cookies.fetch(:timezone, nil)
end
end
# app/views/articles/show.html.erb
<p><strong>Title:</strong> <%= article.title %></p>
<p><strong>Text:</strong> <%= article.text %></p>
<p>Created <%= I18n.l(article.created_at, format: :short) %>,
updated <%= I18n.l(article.updated_at, format: :short) %></p>
Conclusion
Next time, I will explain to you what is provided by I18n.l
. Stay tuned, but patient …