On Jun 20, 2012, at 1:32 PM, cschulte22 wrote:
> What version of rails? Can you post a few lines of the code? The way that
> you're setting your timestamp field may be causing the problem...
3.1.3
I’m not doing anything particularly clever. I’m creating a record based on
params. I don’t explicitly set the timestamp, so it’s just being set by Rails
automatically.
I can see in the database that the timestamp is set to local time.
I read all over the place how Rails will stores all values in UTC, but this
just doesn’t appear to be true. The relevant logic is in
ActiveRecord::ConnectionAdapters::Quoting:
def quoted_date(value)
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc
? :getutc : :getlocal
value.respond_to?(zone_conversion_method) ?
value.send(zone_conversion_method) : value
else
value
end.to_s(:db)
end
On the third line of the above, my ActiveRecord::Base.default_timezone is not
:utc, so it does :get local. It’s writing the local time to the column.
The corresponding logic when reading a timestamp is in
ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods::define_method_attribute:
def define_method_attribute(attr_name)
if create_time_zone_conversion_attribute?(attr_name,
columns_hash[attr_name])
method_body, line = <<-EOV, __LINE__ + 1
def _#{attr_name}
cached = @attributes_cache['#{attr_name}']
return cached if cached
time = _read_attribute('#{attr_name}')
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ?
time.in_time_zone : time
end
alias #{attr_name} _#{attr_name}
EOV
generated_attribute_methods.module_eval(method_body, __FILE__,
line)
else
super
end
end
this calls ActiveRecord::AttributeMethods::Read::ClassMethods:: _read_attribute:
def _read_attribute(attr_name)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id'
value = @attributes[attr_name]
unless value.nil?
if column = column_for_attribute(attr_name)
if unserializable_attribute?(attr_name, column)
unserialize_attribute(attr_name)
else
column.type_cast(value)
end
else
value
end
end
end
which trampolines down to fast_string_to_time:
# Doesn't handle time zones.
def fast_string_to_time(string)
if string =~ Format::ISO_DATETIME
microsec = ($7.to_f * 1_000_000).to_i
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i,
microsec
end
end
which gets back to define_method_attribute, which then does:
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ?
time.in_time_zone : time
this pulls the default time zone (Pacific) and converts my value to Pacific.
But the value in the database is already in Pacific time, so I’m screwed.
Is it just me, or is there a bug here? The thing that bothers me is that this
seems like a pretty major issue not to have been reported and fixed before now.
--
SD Ruby mailing list
[email protected]
http://groups.google.com/group/sdruby