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

Changeset 9040

Show
Ignore:
Timestamp:
03/17/08 02:40:28 (4 months ago)
Author:
gbuesing
Message:

TimeWithZone caches TZInfo::TimezonePeriod used for time conversion so that it can be reused, and enforces DST rules correctly when instance is created from a local time

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activesupport/CHANGELOG

    r9013 r9040  
    11*SVN* 
     2 
     3* TimeWithZone caches TZInfo::TimezonePeriod used for time conversion so that it can be reused, and enforces DST rules correctly when instance is created from a local time [Geoff Buesing] 
    24 
    35* Fixed that BufferedLogger should create its own directory if one doesn't already exist #11285 [lotswholetime] 
  • trunk/activesupport/lib/active_support/time_with_zone.rb

    r9008 r9040  
    66    attr_reader :time_zone 
    77   
    8     def initialize(utc_time, time_zone, local_time = nil) 
    9       @utc = utc_time 
    10       @time = local_time 
    11       @time_zone = time_zone 
     8    def initialize(utc_time, time_zone, local_time = nil, period = nil) 
     9      @utc, @time_zone, @time = utc_time, time_zone, local_time 
     10      @period = @utc ? period : get_period_and_ensure_valid_local_time 
    1211    end 
    1312   
    1413    # Returns a Time instance that represents the time in time_zone 
    1514    def time 
    16       @time ||= time_zone.utc_to_local(@utc) 
     15      @time ||= utc_to_local 
    1716    end 
    1817 
    1918    # Returns a Time instance that represents the time in UTC 
    2019    def utc 
    21       @utc ||= time_zone.local_to_utc(@time) 
     20      @utc ||= local_to_utc 
    2221    end 
    2322    alias_method :comparable_time, :utc 
     
    2625    alias_method :gmtime, :utc 
    2726   
    28     # Returns the underlying TZInfo::TimezonePeriod for the local time 
     27    # Returns the underlying TZInfo::TimezonePeriod 
    2928    def period 
    30       @period ||= time_zone.period_for_utc(utc) 
     29      @period ||= time_zone.period_for_utc(@utc) 
    3130    end 
    3231 
     
    215214      result 
    216215    end 
     216     
     217    private       
     218      def get_period_and_ensure_valid_local_time 
     219        @time_zone.period_for_local(@time) 
     220      rescue ::TZInfo::PeriodNotFound 
     221        # time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again 
     222        @time += 1.hour 
     223        retry 
     224      end 
     225     
     226      # Replicating logic from TZInfo::Timezone#utc_to_local because we want to cache the period in an instance variable for reuse 
     227      def utc_to_local 
     228        ::TZInfo::TimeOrDateTime.wrap(utc) {|utc| period.to_local(utc)} 
     229      end 
     230       
     231      # Replicating logic from TZInfo::Timezone#local_to_utc because we want to cache the period in an instance variable for reuse 
     232      def local_to_utc 
     233        ::TZInfo::TimeOrDateTime.wrap(time) {|time| period.to_utc(time)} 
     234      end 
    217235  end 
    218236end 
  • trunk/activesupport/lib/active_support/values/time_zone.rb

    r9007 r9040  
    184184    #   Time.zone.local(2007, 2, 1, 15, 30, 45)   # => Thu, 01 Feb 2007 15:30:45 HST -10:00 
    185185    def local(*args) 
    186       t = Time.utc_time(*args) 
    187       begin 
    188         result = local_to_utc(t) 
    189       rescue TZInfo::PeriodNotFound 
    190         t += 1.hour 
    191         retry 
    192       end 
    193       result.in_time_zone(self) 
     186      time = Time.utc_time(*args) 
     187      ActiveSupport::TimeWithZone.new(nil, self, time) 
    194188    end 
    195189     
  • trunk/activesupport/test/core_ext/time_with_zone_test.rb

    r9008 r9040  
    256256      assert_equal 17, twz.sec 
    257257      assert_equal 500, twz.usec 
     258    end 
     259     
     260    def test_utc_to_local_conversion_saves_period_in_instance_variable 
     261      assert_nil @twz.instance_variable_get('@period') 
     262      @twz.time 
     263      assert_kind_of TZInfo::TimezonePeriod, @twz.instance_variable_get('@period') 
     264    end 
     265     
     266    def test_instance_created_with_local_time_returns_correct_utc_time 
     267      twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 19)) 
     268      assert_equal Time.utc(2000), twz.utc 
     269    end 
     270     
     271    def test_instance_created_with_local_time_enforces_spring_dst_rules 
     272      twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,2)) # first second of DST 
     273      assert_equal Time.utc(2006,4,2,3), twz.time # springs forward to 3AM 
     274      assert_equal true, twz.dst? 
     275      assert_equal 'EDT', twz.zone 
     276    end 
     277     
     278    def test_instance_created_with_local_time_enforces_fall_dst_rules 
     279      twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,1)) # 1AM can be either DST or non-DST; we'll pick DST 
     280      assert_equal Time.utc(2006,10,29,1), twz.time 
     281      assert_equal true, twz.dst? 
     282      assert_equal 'EDT', twz.zone 
    258283    end 
    259284  end