chris finne

Rails 3 to_s(:db) when database time is NOT UTC

23 Sep 2011

I’m upgrading a Rails application from v2.x to v3.1 and the application stores its dates in MySQL in Pacific timezone rather than UTC.

I’ve set my config/application.rb with the following settings

config.time_zone = 'Pacific Time (US & Canada)'
config.active_record.default_timezone = :local

However, I just discovered that ActiveSupport time extensions don’t really care about ActiveRecord’s default_timezone setting.

=> "2011-09-23 03:02:52"
rails31>> 1.second.ago.to_s(:db)
=> "2011-09-23 10:02:59"

WHAT??? I know that these are 2 different classes:

=> Time
rails31>> 1.second.ago.class
=> ActiveSupport::TimeWithZone

This is because ActiveSupport sets to UTC before doing the “to_s” which makes sense with the default Rails configuration where all dates are stored in the DB as UTC.

# :db format outputs time in UTC; all others output time in local.
# Uses TimeWithZone's +strftime+, so %Z and %z work correctly.
def to_s(format = :default)
  if format == :db
  elsif formatter = ::Time::DATE_FORMATS[format]
    formatter.respond_to?(:call) ? : strftime(formatter)
    "#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
alias_method :to_formatted_s, :to_s

I guess I understand this because I’m dealing with 2 different libraries: ActiveRecord vs ActiveSupport, but it sure threw me for a loop in some of my tests where I would sometimes create values with and other times with and the time-zone conversion would fail the test.

My solution to this issue is to start using .to_s(:localdb) rather than .to_s(:db)

Time::DATE_FORMATS.merge!(:localdb=>"%Y-%m-%d %H:%M:%S")
=> "2011-09-23 02:56:34"
=> "2011-09-23 02:56:38"
rails31>> 1.second.ago.to_s(:db)
=> "2011-09-23 10:02:59"
rails31>> 1.second.ago.to_s(:localdb)
=> "2011-09-23 03:02:59"