module TZInfo # A period of time in a timezone where the same offset from UTC applies. # # All the methods that take times accept instances of Time or DateTime as well # as Integer timestamps. class TimezonePeriod # The TimezoneTransition that defines the start of this TimezonePeriod # (may be nil if unbounded). attr_reader :start_transition # The TimezoneTransition that defines the end of this TimezonePeriod # (may be nil if unbounded). attr_reader :end_transition # The TimezoneOffset for this period. attr_reader :offset # Initializes a new TimezonePeriod. # # TimezonePeriod instances should not normally be constructed manually. def initialize(start_transition, end_transition, offset = nil) @start_transition = start_transition @end_transition = end_transition if offset raise ArgumentError, 'Offset specified with transitions' if @start_transition || @end_transition @offset = offset else if @start_transition @offset = @start_transition.offset elsif @end_transition @offset = @end_transition.previous_offset else raise ArgumentError, 'No offset specified and no transitions to determine it from' end end @utc_total_offset_rational = nil end # The base offset of the timezone from UTC in seconds. This does not include # any adjustment made for daylight savings time and will typically remain # constant throughout the year. # # To obtain the currently observed offset from UTC, including the effect of # daylight savings time, use utc_total_offset instead. # # Note that zoneinfo files only include the value of utc_total_offset and a # DST flag. When using ZoneinfoDataSource, the utc_offset will be derived # from changes to the UTC total offset and the DST flag. As a consequence, # utc_total_offset will always be correct, but utc_offset may be inaccurate. # # If you require utc_offset to be accurate, install the tzinfo-data gem and # set RubyDataSource as the DataSource. def utc_offset @offset.utc_offset end # The offset from the time zone's standard time in seconds. Zero # when daylight savings time is not in effect. Non-zero (usually 3600 = 1 # hour) if daylight savings is being observed. # # Note that zoneinfo files only include the value of utc_total_offset and # a DST flag. When using DataSources::ZoneinfoDataSource, the std_offset # will be derived from changes to the UTC total offset and the DST flag. As # a consequence, utc_total_offset will always be correct, but std_offset # may be inaccurate. # # If you require std_offset to be accurate, install the tzinfo-data gem # and set RubyDataSource as the DataSource. def std_offset @offset.std_offset end # The identifier of this period, e.g. "GMT" (Greenwich Mean Time) or "BST" # (British Summer Time) for "Europe/London". The returned identifier is a # symbol. def abbreviation @offset.abbreviation end alias :zone_identifier :abbreviation # Total offset from UTC (seconds). Equal to utc_offset + std_offset. def utc_total_offset @offset.utc_total_offset end # Total offset from UTC (days). Result is a Rational. def utc_total_offset_rational # Thread-safety: It is possible that the value of # @utc_total_offset_rational may be calculated multiple times in # concurrently executing threads. It is not worth the overhead of locking # to ensure that @zone_identifiers is only calculated once. unless @utc_total_offset_rational result = OffsetRationals.rational_for_offset(utc_total_offset) return result if frozen? @utc_total_offset_rational = result end @utc_total_offset_rational end # The start time of the period in UTC as a DateTime. May be nil if unbounded. def utc_start @start_transition ? @start_transition.at.to_datetime : nil end # The start time of the period in UTC as a Time. May be nil if unbounded. def utc_start_time @start_transition ? @start_transition.at.to_time : nil end # The end time of the period in UTC as a DateTime. May be nil if unbounded. def utc_end @end_transition ? @end_transition.at.to_datetime : nil end # The end time of the period in UTC as a Time. May be nil if unbounded. def utc_end_time @end_transition ? @end_transition.at.to_time : nil end # The start time of the period in local time as a DateTime. May be nil if # unbounded. def local_start @start_transition ? @start_transition.local_start_at.to_datetime : nil end # The start time of the period in local time as a Time. May be nil if # unbounded. def local_start_time @start_transition ? @start_transition.local_start_at.to_time : nil end # The end time of the period in local time as a DateTime. May be nil if # unbounded. def local_end @end_transition ? @end_transition.local_end_at.to_datetime : nil end # The end time of the period in local time as a Time. May be nil if # unbounded. def local_end_time @end_transition ? @end_transition.local_end_at.to_time : nil end # true if daylight savings is in effect for this period; otherwise false. def dst? @offset.dst? end # true if this period is valid for the given UTC DateTime; otherwise false. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def valid_for_utc?(utc) utc_after_start?(utc) && utc_before_end?(utc) end # true if the given UTC DateTime is after the start of the period # (inclusive); otherwise false. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def utc_after_start?(utc) !@start_transition || @start_transition.at <= utc end # true if the given UTC DateTime is before the end of the period # (exclusive); otherwise false. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def utc_before_end?(utc) !@end_transition || @end_transition.at > utc end # true if this period is valid for the given local DateTime; otherwise # false. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def valid_for_local?(local) local_after_start?(local) && local_before_end?(local) end # true if the given local DateTime is after the start of the period # (inclusive); otherwise false. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def local_after_start?(local) !@start_transition || @start_transition.local_start_at <= local end # true if the given local DateTime is before the end of the period # (exclusive); otherwise false. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def local_before_end?(local) !@end_transition || @end_transition.local_end_at > local end # Converts a UTC DateTime to local time based on the offset of this period. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def to_local(utc) @offset.to_local(utc) end # Converts a local DateTime to UTC based on the offset of this period. # # Deprecation warning: this method will be removed in TZInfo version 2.0.0. def to_utc(local) @offset.to_utc(local) end # Returns true if this TimezonePeriod is equal to p. This compares the # start_transition, end_transition and offset using ==. def ==(p) p.kind_of?(TimezonePeriod) && start_transition == p.start_transition && end_transition == p.end_transition && offset == p.offset end # Returns true if this TimezonePeriods is equal to p. This compares the # start_transition, end_transition and offset using eql? def eql?(p) p.kind_of?(TimezonePeriod) && start_transition.eql?(p.start_transition) && end_transition.eql?(p.end_transition) && offset.eql?(p.offset) end # Returns a hash of this TimezonePeriod. def hash result = @start_transition.hash ^ @end_transition.hash result ^= @offset.hash unless @start_transition || @end_transition result end # Returns internal object state as a programmer-readable string. def inspect result = "#<#{self.class}: #{@start_transition.inspect},#{@end_transition.inspect}" result << ",#{@offset.inspect}>" unless @start_transition || @end_transition result + '>' end end end