Jonathan Bennett

Doing Time Zones Right

Time Zones are the best right?

The standard advise is to store all dates in UTC which is a great start. This missing instruction is on the input side of things.

When you add a form.datetime_field to your form, this makes a <input type="datetime_local" /> input which does not include any time zone information when submitted. That means you cannot actually interpret what the time actually is.

To solve this, you need to collect the time zone on the form, or as part of the user account:

<%# collect on the user, account, team etc %>
<%= form_with model: @user do |form| %> 
	<%# ... %>

	<%= form.label :time_zone %>
	<%= form.time_zone_select(:time_zone) %>

	<%= form.submit %>
<% end %>

Assuming you do collect the users time zone as part of the sign up process, you can then wrap all of your controller actions so that the times will be interpreted correctly:

class ApplicationController < ActionController::Base
	around_action :set_time_zone, if: :current_user # or current_account, current_team etc
	
	private
	
	def set_time_zone
		Time.use_zone(current_user.time_zone, &block)
	end
end

My final recommendation is to present the UTC times in the local time zone in your views. A library like local_time makes this super easy to set up, gives lots of options for format and using relative durations. This produces an html <time> tag that has the time in UTC but uses javascript to show the time in local time zone in a specified format.

<%= local_time(post.published_at) %>

So there you have it, 4 steps to handle time properly:

  1. Store time in UTC
  2. Collect the user’s time zone
  3. Use the user’s time zone
  4. Display times in the local time zone on the frontend