Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #10982: time_zone_attributes_2.diff

File time_zone_attributes_2.diff, 11.3 kB (added by technoweenie, 5 months ago)
  • activesupport/lib/active_support/core_ext/time/zones.rb

    old new  
    99        end 
    1010         
    1111        module ClassMethods 
     12          attr_accessor :zone_default 
     13           
    1214          def zone 
    13             Thread.current[:time_zone] 
     15            Thread.current[:time_zone] || zone_default 
    1416          end 
    1517 
    1618          # Sets a global default time zone, separate from the system time zone in ENV['TZ'].  
  • activerecord/test/cases/attribute_methods_test.rb

    old new  
    1414    ActiveRecord::Base.attribute_method_suffix *@old_suffixes 
    1515  end 
    1616 
    17  
    1817  def test_match_attribute_method_query_returns_match_data 
    1918    assert_not_nil md = @target.match_attribute_method?('title=') 
    2019    assert_equal 'title', md.pre_match 
     
    9796  def test_only_time_related_columns_are_meant_to_be_cached_by_default 
    9897    expected = %w(datetime timestamp time date).sort 
    9998    assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort 
    100 end 
     99  end 
    101100 
    102101  def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default 
    103102    default_attributes = Topic.cached_attributes 
     
    138137      end 
    139138    end 
    140139  end 
     140   
     141  def test_time_attributes_are_retrieved_in_current_time_zone 
     142    in_time_zone "Pacific Time (US & Canada)" do 
     143      utc_time = Time.utc(2008, 1, 1) 
     144      record   = @target.new 
     145      record[:written_on] = utc_time 
     146      assert_equal utc_time.in_current_time_zone, record.written_on 
     147      assert_kind_of ActiveSupport::TimeWithZone, record.written_on 
     148    end 
     149  end 
     150   
     151  def test_setting_time_zone_aware_attribute_to_utc 
     152    in_time_zone "Pacific Time (US & Canada)" do 
     153      utc_time = Time.utc(2008, 1, 1) 
     154      record   = @target.new 
     155      record.written_on = utc_time 
     156      assert_equal utc_time.in_current_time_zone, record.written_on 
     157    end 
     158  end 
     159   
     160  def test_setting_time_zone_aware_attribute_to_utc_time 
     161    in_time_zone "Pacific Time (US & Canada)" do 
     162      utc_time = Time.utc(2008, 1, 1) 
     163      record   = @target.new 
     164      record.written_on = utc_time 
     165      assert_equal utc_time.in_current_time_zone, record.written_on 
     166    end 
     167  end 
     168   
     169  def test_setting_time_zone_aware_attribute_in_other_time_zone 
     170    utc_time = Time.utc(2008, 1, 1) 
     171    cst_time = utc_time.in_time_zone("Central Time (US & Canada)") 
     172    in_time_zone "Pacific Time (US & Canada)" do 
     173      record   = @target.new 
     174      record.written_on = cst_time 
     175      assert_equal utc_time.in_current_time_zone, record.written_on 
     176    end 
     177  end 
     178   
     179  def test_setting_time_zone_aware_attribute_in_current_time_zone 
     180    utc_time = Time.utc(2008, 1, 1) 
     181    in_time_zone "Pacific Time (US & Canada)" do 
     182      record   = @target.new 
     183      record.written_on = utc_time.in_current_time_zone 
     184      assert_equal utc_time.in_current_time_zone, record.written_on 
     185    end 
     186  end 
    141187 
    142188  private 
    143189  def time_related_columns_on_topic 
    144190    Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name) 
    145191  end 
     192   
     193  def in_time_zone(zone) 
     194    old_zone  = Time.zone 
     195    old_tz    = ActiveRecord::Base.time_zone_aware_attributes 
     196 
     197    Time.zone = zone ? TimeZone[zone] : nil 
     198    ActiveRecord::Base.time_zone_aware_attributes = !zone.nil? 
     199    yield 
     200  ensure 
     201    Time.zone = old_zone 
     202    ActiveRecord::Base.time_zone_aware_attributes = old_tz 
     203  end 
    146204end 
  • activerecord/test/cases/base_test.rb

    old new  
    924924    assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on 
    925925  end 
    926926 
     927  def test_multiparameter_attributes_on_time_with_utc 
     928    ActiveRecord::Base.default_timezone = :utc 
     929    attributes = { 
     930      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", 
     931      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" 
     932    } 
     933    topic = Topic.find(1) 
     934    topic.attributes = attributes 
     935    assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on 
     936  ensure 
     937    ActiveRecord::Base.default_timezone = :local 
     938  end 
     939 
     940  def test_multiparameter_attributes_on_time_with_time_zone 
     941    ActiveRecord::Base.default_timezone = :utc 
     942    Time.zone = TimeZone[-28800] 
     943    attributes = { 
     944      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", 
     945      "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" 
     946    } 
     947    topic = Topic.find(1) 
     948    topic.attributes = attributes 
     949    assert_equal Time.utc(2004, 6, 24, 23, 24, 0).in_current_time_zone, topic.written_on 
     950  ensure 
     951    ActiveRecord::Base.default_timezone = :local 
     952    Time.zone = nil 
     953  end 
     954 
    927955  def test_multiparameter_attributes_on_time_with_empty_seconds 
    928956    attributes = { 
    929957      "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", 
  • activerecord/lib/active_record/attribute_methods.rb

    old new  
    88      base.attribute_method_suffix(*DEFAULT_SUFFIXES) 
    99      base.cattr_accessor :attribute_types_cached_by_default, :instance_writer => false 
    1010      base.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT 
     11      base.cattr_accessor :time_zone_aware_attributes, :instance_writer => false 
     12      base.time_zone_aware_attributes = false 
     13      base.cattr_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false 
     14      base.skip_time_zone_conversion_for_attributes = [] 
    1115    end 
    1216 
    1317    # Declare and check for suffixed attribute methods. 
     
    6468          unless instance_method_already_implemented?(name) 
    6569            if self.serialized_attributes[name] 
    6670              define_read_method_for_serialized_attribute(name) 
     71            elsif create_time_zone_conversion_attribute?(name, column) 
     72              define_read_method_for_time_zone_conversion(name) 
    6773            else 
    6874              define_read_method(name.to_sym, name, column) 
    6975            end 
    7076          end 
    7177 
    7278          unless instance_method_already_implemented?("#{name}=") 
    73             define_write_method(name.to_sym) 
     79            if create_time_zone_conversion_attribute?(name, column) 
     80              define_write_method_for_time_zone_conversion(name) 
     81            else   
     82              define_write_method(name.to_sym) 
     83            end 
    7484          end 
    7585 
    7686          unless instance_method_already_implemented?("#{name}?") 
     
    121131          @@attribute_method_suffixes ||= [] 
    122132        end 
    123133         
     134        def create_time_zone_conversion_attribute?(name, column) 
     135          time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type) 
     136        end 
     137         
    124138        # Define an attribute reader method.  Cope with nil column. 
    125139        def define_read_method(symbol, attr_name, column) 
    126140          cast_code = column.type_cast_code('v') if column 
     
    140154        def define_read_method_for_serialized_attribute(attr_name) 
    141155          evaluate_attribute_method attr_name, "def #{attr_name}; unserialize_attribute('#{attr_name}'); end" 
    142156        end 
     157         
     158        def define_read_method_for_time_zone_conversion(attr_name) 
     159          method_body = <<-EOV 
     160            def #{attr_name}(reload = false) 
     161              cached = @attributes_cache['#{attr_name}'] 
     162              return cached if cached && !reload 
     163              time = read_attribute('#{attr_name}') 
     164              @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_current_time_zone : time 
     165            end 
     166          EOV 
     167          evaluate_attribute_method attr_name, method_body 
     168        end 
    143169 
    144170        # Define an attribute ? method. 
    145171        def define_question_method(attr_name) 
     
    149175        def define_write_method(attr_name) 
    150176          evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}=" 
    151177        end 
     178         
     179        def define_write_method_for_time_zone_conversion(attr_name) 
     180          method_body = <<-EOV 
     181            def #{attr_name}=(time) 
     182              if time 
     183                time = time.to_time rescue time unless time.acts_like?(:time) 
     184                time = time.in_current_time_zone if time.acts_like?(:time) 
     185              end 
     186              write_attribute(:#{attr_name}, time) 
     187            end 
     188          EOV 
     189          evaluate_attribute_method attr_name, method_body, "#{attr_name}=" 
     190        end 
    152191 
    153192        # Evaluate the definition for an attribute related method 
    154193        def evaluate_attribute_method(attr_name, method_definition, method_name=attr_name) 
     
    303342      end 
    304343      super 
    305344    end 
    306      
    307345 
    308346    private 
    309347     
  • activerecord/lib/active_record/base.rb

    old new  
    24712471 
    24722472      # Includes an ugly hack for Time.local instead of Time.new because the latter is reserved by Time itself. 
    24732473      def instantiate_time_object(*values) 
    2474         @@default_timezone == :utc ? Time.utc(*values) : Time.local(*values) 
     2474        if Time.zone 
     2475          Time.local(*values).change_time_zone_to_current 
     2476        else 
     2477          @@default_timezone == :utc ? Time.utc(*values) : Time.local(*values) 
     2478        end 
    24752479      end 
    24762480 
    24772481      def execute_callstack_for_multiparameter_attributes(callstack) 
  • railties/lib/initializer.rb

    old new  
    8181      initialize_dependency_mechanism 
    8282      initialize_whiny_nils 
    8383      initialize_temporary_session_directory 
     84      initialize_time_zone 
    8485      initialize_framework_settings 
    8586 
    8687      add_support_load_paths 
     
    316317      end 
    317318    end 
    318319 
     320    def initialize_time_zone 
     321      if configuration.time_zone 
     322        Time.zone_default = TimeZone[configuration.time_zone] 
     323        if configuration.frameworks.include?(:active_record) 
     324          ActiveRecord::Base.time_zone_aware_attributes = true 
     325          ActiveRecord::Base.default_timezone = :utc 
     326        end 
     327      end 
     328    end 
     329 
    319330    # Initializes framework-specific settings for each of the loaded frameworks 
    320331    # (Configuration#frameworks). The available settings map to the accessors 
    321332    # on each of the corresponding Base classes. 
     
    456467    end 
    457468    alias_method :breakpoint_server=, :breakpoint_server 
    458469 
     470    # Sets the default time_zone.  Setting this will enable time_zone 
     471    # awareness for ActiveRecord models and set the ActiveRecord default 
     472    # timezone to :utc. 
     473    attr_accessor :time_zone 
     474 
    459475    # Create a new Configuration instance, initialized with the default 
    460476    # values. 
    461477    def initialize