I’ve posted a bug report:
<https://github.com/rails/rails/issues/6816>
On Jun 20, 2012, at 5:26 PM, Guyren Howe wrote:
> 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