Time Zones in Rails: A Junior Dev's Survival Guide
One of the most confusing aspects for new Rails developers is handling time zones properly. Let's break it down into simple, practical examples.
The Basics: Setting Up Time Zones
# config/application.rb
class Application < Rails::Application
# ❌ Bad: Using default time zone
# This will use UTC and cause confusion
# ✅ Good: Setting application time zone
config.time_zone = 'Eastern Time (US & Canada)'
# This is crucial! It converts DB times to UTC automatically
config.active_record.default_timezone = :utc
end
Common Time Zone Mistakes and Fixes
1. Creating Events or Appointments
# ❌ Bad: Ignoring time zones
class Event < ApplicationRecord
def schedule_for(date_string)
update(start_time: Time.parse(date_string))
end
end
# ✅ Good: Using Time.zone
class Event < ApplicationRecord
def schedule_for(date_string)
update(start_time: Time.zone.parse(date_string))
end
end
2. Displaying Times
# app/helpers/application_helper.rb
module ApplicationHelper
# ❌ Bad: Using strftime directly
def format_time(time)
time.strftime("%B %d, %Y at %I:%M %p")
end
# ✅ Good: Using I18n with time zones
def format_time(time)
I18n.l(time.in_time_zone(current_user.time_zone),
format: :long)
end
end
3. Date/Time Comparisons
# ❌ Bad: Comparing times without zones
def today_appointments
appointments.where('DATE(start_time) = ?', Date.today)
end
# ✅ Good: Using time zones properly
def today_appointments
appointments.where(
start_time: Time.zone.now.beginning_of_day..Time.zone.now.end_of_day
)
end
Pro Tips for Time Zones
Testing Different Time Zones
# spec/models/event_spec.rb
describe Event do
it "creates events in user's time zone" do
Time.use_zone('Pacific Time (US & Canada)') do
event = Event.create!(start_time: Time.zone.parse('2024-01-01 14:00'))
expect(event.start_time.zone).to eq('UTC')
expect(event.start_time.in_time_zone('Pacific Time (US & Canada)').hour).to eq(14)
end
end
end
User-Specific Time Zones
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :set_time_zone, if: :current_user
private
def set_time_zone
Time.use_zone(current_user.time_zone) { yield }
end
end