File: active_support/vendor/tzinfo-0.3.12/tzinfo/data_timezone_info.rb

Overview
Module Structure
Class Hierarchy
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: TZInfo#29
  class: NoOffsetsDefined#32
inherits from
  StandardError ( Builtin-Module )
  class: DataTimezoneInfo#36
inherits from
  TimezoneInfo ( TZInfo )
has properties
method: initialize / 1 #39
method: offset / 4 #57
method: transition / 5 #71
method: period_for_utc / 1 #104
method: periods_for_local / 1 #148
method: transition_index / 2 #194
method: transition_after_start / 1 #204
method: transition_before_end / 1 #216

Class Hierarchy

Code

   1  #--
   2  # Copyright (c) 2006 Philip Ross
   3  # 
   4  # Permission is hereby granted, free of charge, to any person obtaining a copy
   5  # of this software and associated documentation files (the "Software"), to deal
   6  # in the Software without restriction, including without limitation the rights
   7  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8  # copies of the Software, and to permit persons to whom the Software is
   9  # furnished to do so, subject to the following conditions:
  10  # 
  11  # The above copyright notice and this permission notice shall be included in all
  12  # copies or substantial portions of the Software.
  13  #
  14  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20  # THE SOFTWARE.
  21  #++
  22 
  23  require 'tzinfo/time_or_datetime'
  24  require 'tzinfo/timezone_info'
  25  require 'tzinfo/timezone_offset_info'
  26  require 'tzinfo/timezone_period'
  27  require 'tzinfo/timezone_transition_info'
  28 
  29  module TZInfo
  30    # Thrown if no offsets have been defined when calling period_for_utc or
  31    # periods_for_local. Indicates an error in the timezone data.
  32    class NoOffsetsDefined < StandardError
  33    end
  34      
  35    # Represents a (non-linked) timezone defined in a data module.
  36    class DataTimezoneInfo < TimezoneInfo #:nodoc:
  37              
  38      # Constructs a new TimezoneInfo with its identifier.
  39      def initialize(identifier)   
  40        super(identifier)
  41        @offsets = {}
  42        @transitions = []
  43        @previous_offset = nil
  44        @transitions_index = nil
  45      end
  46   
  47      # Defines a offset. The id uniquely identifies this offset within the
  48      # timezone. utc_offset and std_offset define the offset in seconds of 
  49      # standard time from UTC and daylight savings from standard time 
  50      # respectively. abbreviation describes the timezone offset (e.g. GMT, BST,
  51      # EST or EDT).
  52      #
  53      # The first offset to be defined is treated as the offset that applies
  54      # until the first transition. This will usually be in Local Mean Time (LMT).
  55      #
  56      # ArgumentError will be raised if the id is already defined.
  57      def offset(id, utc_offset, std_offset, abbreviation)
  58        raise ArgumentError, 'Offset already defined' if @offsets.has_key?(id)
  59        
  60        offset = TimezoneOffsetInfo.new(utc_offset, std_offset, abbreviation)
  61        @offsets[id] = offset
  62        @previous_offset = offset unless @previous_offset
  63      end
  64      
  65      # Defines a transition. Transitions must be defined in chronological order.
  66      # ArgumentError will be raised if a transition is added out of order.
  67      # offset_id refers to an id defined with offset. ArgumentError will be 
  68      # raised if the offset_id cannot be found. numerator_or_time and
  69      # denomiator specify the time the transition occurs as. See 
  70      # TimezoneTransitionInfo for more detail about specifying times.
  71      def transition(year, month, offset_id, numerator_or_time, denominator = nil)
  72        offset = @offsets[offset_id]      
  73        raise ArgumentError, 'Offset not found' unless offset
  74              
  75        if @transitions_index
  76          if year < @last_year || (year == @last_year && month < @last_month)
  77            raise ArgumentError, 'Transitions must be increasing date order'
  78          end
  79          
  80          # Record the position of the first transition with this index.
  81          index = transition_index(year, month)
  82          @transitions_index[index] ||= @transitions.length
  83                  
  84          # Fill in any gaps       
  85          (index - 1).downto(0) do |i|
  86            break if @transitions_index[i]
  87            @transitions_index[i] = @transitions.length
  88          end
  89        else
  90          @transitions_index = [@transitions.length]
  91          @start_year = year
  92          @start_month = month        
  93        end
  94        
  95        @transitions << TimezoneTransitionInfo.new(offset, @previous_offset, 
  96          numerator_or_time, denominator)
  97        @last_year = year
  98        @last_month = month             
  99        @previous_offset = offset
 100      end           
 101      
 102      # Returns the TimezonePeriod for the given UTC time.
 103      # Raises NoOffsetsDefined if no offsets have been defined.
 104      def period_for_utc(utc)
 105        unless @transitions.empty?
 106          utc = TimeOrDateTime.wrap(utc)               
 107          index = transition_index(utc.year, utc.mon)
 108          
 109          start_transition = nil
 110          start = transition_before_end(index)
 111          if start
 112            start.downto(0) do |i|
 113              if @transitions[i].at <= utc
 114                start_transition = @transitions[i]
 115                break
 116              end
 117            end
 118          end
 119          
 120          end_transition = nil
 121          start = transition_after_start(index)      
 122          if start
 123            start.upto(@transitions.length - 1) do |i|
 124              if @transitions[i].at > utc
 125                end_transition = @transitions[i]
 126                break
 127              end
 128            end
 129          end
 130                 
 131          if start_transition || end_transition
 132            TimezonePeriod.new(start_transition, end_transition)
 133          else
 134            # Won't happen since there are transitions. Must always find one
 135            # transition that is either >= or < the specified time.
 136            raise 'No transitions found in search'
 137          end        
 138        else
 139          raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset        
 140          TimezonePeriod.new(nil, nil, @previous_offset)
 141        end
 142      end
 143      
 144      # Returns the set of TimezonePeriods for the given local time as an array.    
 145      # Results returned are ordered by increasing UTC start date.
 146      # Returns an empty array if no periods are found for the given time.
 147      # Raises NoOffsetsDefined if no offsets have been defined.    
 148      def periods_for_local(local)
 149        unless @transitions.empty?
 150          local = TimeOrDateTime.wrap(local)
 151          index = transition_index(local.year, local.mon)
 152          
 153          result = []
 154         
 155          start_index = transition_after_start(index - 1)       
 156          if start_index && @transitions[start_index].local_end > local           
 157            if start_index > 0
 158              if @transitions[start_index - 1].local_start <= local 
 159                result << TimezonePeriod.new(@transitions[start_index - 1], @transitions[start_index])
 160              end
 161            else
 162              result << TimezonePeriod.new(nil, @transitions[start_index])
 163            end
 164          end
 165          
 166          end_index = transition_before_end(index + 1)
 167          
 168          if end_index        
 169            start_index = end_index unless start_index
 170            
 171            start_index.upto(transition_before_end(index + 1)) do |i|
 172              if @transitions[i].local_start <= local
 173                if i + 1 < @transitions.length
 174                  if @transitions[i + 1].local_end > local
 175                    result << TimezonePeriod.new(@transitions[i], @transitions[i + 1])
 176                  end
 177                else
 178                  result << TimezonePeriod.new(@transitions[i], nil)
 179                end
 180              end
 181            end
 182          end
 183          
 184          result
 185        else
 186          raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
 187          [TimezonePeriod.new(nil, nil, @previous_offset)]
 188        end
 189      end
 190      
 191      private
 192        # Returns the index into the @transitions_index array for a given year 
 193        # and month.
 194        def transition_index(year, month)
 195          index = (year - @start_year) * 2
 196          index += 1 if month > 6
 197          index -= 1 if @start_month > 6
 198          index
 199        end
 200        
 201        # Returns the index into @transitions of the first transition that occurs
 202        # on or after the start of the given index into @transitions_index.
 203        # Returns nil if there are no such transitions.
 204        def transition_after_start(index)
 205          if index >= @transitions_index.length
 206            nil
 207          else
 208            index = 0 if index < 0
 209            @transitions_index[index]
 210          end
 211        end
 212        
 213        # Returns the index into @transitions of the first transition that occurs
 214        # before the end of the given index into @transitions_index.
 215        # Returns nil if there are no such transitions.
 216        def transition_before_end(index)
 217          index = index + 1
 218          
 219          if index <= 0
 220            nil
 221          elsif index >= @transitions_index.length          
 222            @transitions.length - 1
 223          else      
 224            @transitions_index[index] - 1          
 225          end
 226        end            
 227    end
 228  end