aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb42
-rwxr-xr-xactiverecord/lib/active_record/base.rb12
2 files changed, 48 insertions, 6 deletions
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 657c575866..f976e70a06 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -8,6 +8,10 @@ module ActiveRecord
base.attribute_method_suffix(*DEFAULT_SUFFIXES)
base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false
base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
+ base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false
+ base.time_zone_aware_attributes = false
+ base.cattr_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ base.skip_time_zone_conversion_for_attributes = []
end
# Declare and check for suffixed attribute methods.
@@ -64,13 +68,19 @@ module ActiveRecord
unless instance_method_already_implemented?(name)
if self.serialized_attributes[name]
define_read_method_for_serialized_attribute(name)
+ elsif create_time_zone_conversion_attribute?(name, column)
+ define_read_method_for_time_zone_conversion(name)
else
define_read_method(name.to_sym, name, column)
end
end
unless instance_method_already_implemented?("#{name}=")
- define_write_method(name.to_sym)
+ if create_time_zone_conversion_attribute?(name, column)
+ define_write_method_for_time_zone_conversion(name)
+ else
+ define_write_method(name.to_sym)
+ end
end
unless instance_method_already_implemented?("#{name}?")
@@ -121,6 +131,10 @@ module ActiveRecord
@@attribute_method_suffixes ||= []
end
+ def create_time_zone_conversion_attribute?(name, column)
+ time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
+ end
+
# Define an attribute reader method. Cope with nil column.
def define_read_method(symbol, attr_name, column)
cast_code = column.type_cast_code('v') if column
@@ -140,6 +154,18 @@ module ActiveRecord
def define_read_method_for_serialized_attribute(attr_name)
evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end"
end
+
+ def define_read_method_for_time_zone_conversion(attr_name)
+ method_body = <<-EOV
+ def #{attr_name}(reload = false)
+ cached = @attributes_cache['#{attr_name}']
+ return cached if cached && !reload
+ time = read_attribute('#{attr_name}')
+ @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_current_time_zone : time
+ end
+ EOV
+ evaluate_attribute_method attr_name, method_body
+ end
# Define an attribute ? method.
def define_question_method(attr_name)
@@ -149,6 +175,19 @@ module ActiveRecord
def define_write_method(attr_name)
evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
end
+
+ def define_write_method_for_time_zone_conversion(attr_name)
+ method_body = <<-EOV
+ def #{attr_name}=(time)
+ if time
+ time = time.to_time rescue time unless time.acts_like?(:time)
+ time = time.in_current_time_zone if time.acts_like?(:time)
+ end
+ write_attribute(:#{attr_name}, time)
+ end
+ EOV
+ evaluate_attribute_method attr_name, method_body, "#{attr_name}="
+ end
# Evaluate the definition for an attribute related method
def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name)
@@ -303,7 +342,6 @@ module ActiveRecord
end
super
end
-
private
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index f1759aa68f..d1729b6ff4 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -2470,8 +2470,12 @@ module ActiveRecord #:nodoc:
end
# Includes an ugly hack for Time.local instead of Time.new because the latter is reserved by Time itself.
- def instantiate_time_object(*values)
- @@default_timezone == :utc ? Time.utc(*values) : Time.local(*values)
+ def instantiate_time_object(name, values)
+ if Time.zone && !self.class.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
+ Time.zone.new(*values)
+ else
+ @@default_timezone == :utc ? Time.utc(*values) : Time.local(*values)
+ end
end
def execute_callstack_for_multiparameter_attributes(callstack)
@@ -2483,12 +2487,12 @@ module ActiveRecord #:nodoc:
else
begin
value = if Time == klass
- instantiate_time_object(*values)
+ instantiate_time_object(name, values)
elsif Date == klass
begin
Date.new(*values)
rescue ArgumentError => ex # if Date.new raises an exception on an invalid date
- instantiate_time_object(*values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
+ instantiate_time_object(name, values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
end
else
klass.new(*values)