diff --git a/lib/tzinfo/zoneinfo_timezone_info.rb b/lib/tzinfo/zoneinfo_timezone_info.rb index 9061e4e7..6273ebfb 100644 --- a/lib/tzinfo/zoneinfo_timezone_info.rb +++ b/lib/tzinfo/zoneinfo_timezone_info.rb @@ -56,39 +56,109 @@ def check_read(file, bytes) result end - # Zoneinfo doesn't include the offset from standard time (std_offset). - # Derive the missing offsets by looking at changes in the total UTC - # offset. + # Zoneinfo files don't include the offset from standard time (std_offset) + # for DST periods. Derive the base offset (utc_offset) where DST is + # observed from either the previous or next non-DST period. # - # This will be run through forwards and then backwards by the parse - # method. - def derive_offsets(transitions, offsets) - previous_offset = nil + # Returns the index of the offset to be used prior to the first + # transition. + def derive_offsets(transitions, offsets) + # The first non-DST offset (if there is one) is the offset observed + # before the first transition. Fallback to the first DST offset if there + # are no non-DST offsets. + first_non_dst_offset_index = offsets.index {|o| !o[:is_dst] } + first_offset = first_non_dst_offset_index || 0 + return first_offset if transitions.empty? - transitions.each do |t| - offset = offsets[t[:offset]] + # Determine the utc_offset of the next non-dst offset at each transition. + utc_offset_from_next = nil - if !offset[:std_offset] && offset[:is_dst] && previous_offset - difference = offset[:utc_total_offset] - previous_offset[:utc_total_offset] - - if previous_offset[:is_dst] - if previous_offset[:std_offset] - std_offset = previous_offset[:std_offset] + difference - else - std_offset = nil - end - else - std_offset = difference + transitions.reverse_each do |transition| + offset = offsets[transition[:offset]] + if offset[:is_dst] + transition[:utc_offset_from_next] = utc_offset_from_next if utc_offset_from_next + else + utc_offset_from_next = offset[:utc_total_offset] + end + end + + utc_offset_from_previous = first_non_dst_offset_index ? offsets[first_non_dst_offset_index][:utc_total_offset] : nil + defined_offsets = {} + + transitions.each do |transition| + offset_index = transition[:offset] + offset = offsets[offset_index] + utc_total_offset = offset[:utc_total_offset] + + if offset[:is_dst] + utc_offset_from_next = transition[:utc_offset_from_next] + + difference_to_previous = utc_total_offset - (utc_offset_from_previous || utc_total_offset) + difference_to_next = utc_total_offset - (utc_offset_from_next || utc_total_offset) + + utc_offset = if difference_to_previous > 0 && difference_to_next > 0 + difference_to_previous < difference_to_next ? utc_offset_from_previous : utc_offset_from_next + elsif difference_to_previous > 0 + utc_offset_from_previous + elsif difference_to_next > 0 + utc_offset_from_next + else # difference_to_previous <= 0 && difference_to_next <= 0 + # DST, but the either the offset has stayed the same or decreased + # relative to both the previous and next used base utc offset, or + # there are no non-DST offsets. Assume a 1 hour offset from base. + utc_total_offset - 3600 end - - if std_offset && std_offset > 0 - offset[:std_offset] = std_offset - offset[:utc_offset] = offset[:utc_total_offset] - std_offset + + if !offset[:utc_offset] + offset[:utc_offset] = utc_offset + defined_offsets[offset] = offset_index + elsif offset[:utc_offset] != utc_offset + # An earlier transition has already derived a different + # utc_offset. Define a new offset or reuse an existing identically + # defined offset. + new_offset = offset.dup + new_offset[:utc_offset] = utc_offset + + offset_index = defined_offsets[new_offset] + + unless offset_index + offsets << new_offset + offset_index = offsets.length - 1 + defined_offsets[new_offset] = offset_index + end + + transition[:offset] = offset_index end + else + utc_offset_from_previous = utc_total_offset end - - previous_offset = offset end + + first_offset + end + + # Defines an offset for the timezone based on the given index and offset + # Hash. + def define_offset(index, offset) + utc_total_offset = offset[:utc_total_offset] + utc_offset = offset[:utc_offset] + + if utc_offset + # DST offset with base utc_offset derived by derive_offsets. + std_offset = utc_total_offset - utc_offset + elsif offset[:is_dst] + # DST offset unreferenced by a transition (offset in use before the + # first transition). No derived base UTC offset, so assume 1 hour + # DST. + utc_offset = utc_total_offset - 3600 + std_offset = 3600 + else + # Non-DST offset. + utc_offset = utc_total_offset + std_offset = 0 + end + + offset index, utc_offset, std_offset, offset[:abbr].untaint.to_sym end # Parses a zoneinfo file and intializes the DataTimezoneInfo structures. @@ -179,33 +249,12 @@ def parse(file) end # Derive the offsets from standard time (std_offset). - derive_offsets(transitions, offsets) - derive_offsets(transitions.reverse, offsets) + first_offset_index = derive_offsets(transitions, offsets) - # Assign anything left a standard offset of one hour - offsets.each do |o| - if !o[:std_offset] && o[:is_dst] - o[:std_offset] = 3600 - o[:utc_offset] = o[:utc_total_offset] - 3600 - end - end - - # Find the first non-dst offset. This is used as the offset for the time - # before the first transition. - first = nil - offsets.each_with_index do |o, i| - if !o[:is_dst] - first = i - break - end - end - - if first - offset first, offsets[first][:utc_offset], offsets[first][:std_offset], offsets[first][:abbr].untaint.to_sym - end + define_offset(first_offset_index, offsets[first_offset_index]) offsets.each_with_index do |o, i| - offset i, o[:utc_offset], o[:std_offset], o[:abbr].untaint.to_sym unless i == first + define_offset(i, o) unless i == first_offset_index end if !using_64bit && !RubyCoreSupport.time_supports_negative diff --git a/test/tc_zoneinfo_timezone_info.rb b/test/tc_zoneinfo_timezone_info.rb index 077e41f5..cc928966 100644 --- a/test/tc_zoneinfo_timezone_info.rb +++ b/test/tc_zoneinfo_timezone_info.rb @@ -707,27 +707,39 @@ def test_load_starts_two_hour_std_offset end end - def test_load_starts_all_same_dst_offset + def test_load_starts_only_dst_transition_with_lmt # The zoneinfo files don't include the offset from standard time, so this # has to be derived by looking at changes in the total UTC offset. - # - # If there are no changes in the UTC offset (ignoring the first offset, - # which is usually local mean time), then a value of 1 hour is used as the - # standard time offset. - + offsets = [ {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, - {:gmtoff => 7200, :isdst => true, :abbrev => 'XDDT'}] - - transitions = [ - {:at => Time.utc(2000, 1, 1), :offset_index => 1}] - - tzif_test(offsets, transitions) do |path, format| - info = ZoneinfoTimezoneInfo.new('Zone/DoubleDaylight', path) - assert_equal('Zone/DoubleDaylight', info.identifier) - - assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) - assert_period(:XDDT, 3600, 3600, true, Time.utc(2000, 1, 1), nil, info) + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}] + + transitions = [{:at => Time.utc(2000, 1, 1), :offset_index => 1}] + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path) + assert_equal('Zone/OnlyDST', info.identifier) + + assert_period(:LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XDT, 3542, 3658, true, Time.utc(2000, 1, 1), nil, info) + end + end + + def test_load_starts_only_dst_transition_without_lmt + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [{:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}] + + transitions = [{:at => Time.utc(2000, 1, 1), :offset_index => 0}] + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/OnlyDST', path) + assert_equal('Zone/OnlyDST', info.identifier) + + assert_period(:XDT, 3600, 3600, true, nil, Time.utc(2000, 1, 1), info) + assert_period(:XDT, 3600, 3600, true, Time.utc(2000, 1, 1), nil, info) end end @@ -756,6 +768,297 @@ def test_load_switch_to_dst_and_change_utc_offset assert_period(:XDT, 0, 3600, true, Time.utc(2000, 2, 1), nil, info) end end + + def test_load_apia_international_dateline_change + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + # Pacific/Apia moved across the International Date Line whilst observing + # daylight savings time. + + offsets = [ + {:gmtoff => 45184, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => -39600, :isdst => false, :abbrev => '-11'}, + {:gmtoff => -36000, :isdst => true, :abbrev => '-10'}, + {:gmtoff => 50400, :isdst => true, :abbrev => '+14'}, + {:gmtoff => 46800, :isdst => false, :abbrev => '+13'}] + + transitions = [ + {:at => Time.utc(2011, 4, 2, 14, 0, 0), :offset_index => 1}, + {:at => Time.utc(2011, 9, 24, 14, 0, 0), :offset_index => 2}, + {:at => Time.utc(2011, 12, 30, 10, 0, 0), :offset_index => 3}, + {:at => Time.utc(2012, 3, 31, 14, 0, 0), :offset_index => 4}] + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Test/Pacific/Apia', path) + assert_equal('Test/Pacific/Apia', info.identifier) + + assert_period( :LMT, 45184, 0, false, nil, Time.utc(2011, 4, 2, 14, 0, 0), info) + assert_period(:'-11', -39600, 0, false, Time.utc(2011, 4, 2, 14, 0, 0), Time.utc(2011, 9, 24, 14, 0, 0), info) + assert_period(:'-10', -39600, 3600, true, Time.utc(2011, 9, 24, 14, 0, 0), Time.utc(2011, 12, 30, 10, 0, 0), info) + assert_period(:'+14', 46800, 3600, true, Time.utc(2011, 12, 30, 10, 0, 0), Time.utc(2012, 3, 31, 14, 0, 0), info) + assert_period(:'+13', 46800, 0, false, Time.utc(2012, 3, 31, 14, 0, 0), nil, info) + end + end + + def test_load_offset_split_for_different_utc_offset + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 1}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 1}, + {:at => Time.utc(2000, 4, 1), :offset_index => 2}, + {:at => Time.utc(2000, 5, 1), :offset_index => 3}, + {:at => Time.utc(2000, 6, 1), :offset_index => 2}, + {:at => Time.utc(2000, 7, 1), :offset_index => 1}, + {:at => Time.utc(2000, 8, 1), :offset_index => 3}, + {:at => Time.utc(2000, 9, 1), :offset_index => 1}, + {:at => Time.utc(2000, 10, 1), :offset_index => 2}, + {:at => Time.utc(2000, 11, 1), :offset_index => 3}, + {:at => Time.utc(2000, 12, 1), :offset_index => 2}] + + # XDT will be split and defined according to its surrounding standard time + # offsets. + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/SplitUtcOffset', path) + assert_equal('Zone/SplitUtcOffset', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period( :XDT, 3600, 7200, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 3, 1), Time.utc(2000, 4, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 4, 1), Time.utc(2000, 5, 1), info) + assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 5, 1), Time.utc(2000, 6, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 6, 1), Time.utc(2000, 7, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 7, 1), Time.utc(2000, 8, 1), info) + assert_period( :XDT, 3600, 7200, true, Time.utc(2000, 8, 1), Time.utc(2000, 9, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 9, 1), Time.utc(2000, 10, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 10, 1), Time.utc(2000, 11, 1), info) + assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 11, 1), Time.utc(2000, 12, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 12, 1), nil, info) + + 1.upto(6) do |i| + assert_same(info.period_for_utc(Time.utc(2000, i, 1)).offset, info.period_for_utc(Time.utc(2000, i + 6, 1)).offset) + end + end + end + + def test_load_offset_utc_offset_taken_from_minimum_difference_minimum_after + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 1}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 2}] + + # XDT should use the closest utc_offset (7200) (and not an equivalent + # utc_offset of 3600 and std_offset of 7200). + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path) + assert_equal('Zone/MinimumUtcOffset', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 3, 1), nil, info) + end + end + + def test_load_offset_utc_offset_taken_from_minimum_difference_minimum_before + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 2}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 1}] + + # XDT should use the closest utc_offset (7200) (and not an equivalent + # utc_offset of 3600 and std_offset of 7200). + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/MinimumUtcOffset', path) + assert_equal('Zone/MinimumUtcOffset', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period( :XDT, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 3, 1), nil, info) + end + end + + def test_load_offset_does_not_use_equal_utc_total_offset_equal_after + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 1}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 2}] + + # XDT will be based on the utc_offset of XST1 even though XST2 has an + # equivalent (or greater) utc_total_offset. + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path) + assert_equal('Zone/UtcOffsetEqual', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period( :XDT, 3600, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 3, 1), nil, info) + end + end + + def test_load_offset_does_not_use_equal_utc_total_offset_equal_before + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 2}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 1}] + + # XDT will be based on the utc_offset of XST1 even though XST2 has an + # equivalent (or greater) utc_total_offset. + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetEqual', path) + assert_equal('Zone/UtcOffsetEqual', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period( :XDT, 3600, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 3, 1), nil, info) + end + end + + def test_load_offset_both_adjacent_non_dst_equal_utc_total_offset + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 7142, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST'}, + {:gmtoff => 7200, :isdst => true, :abbrev => 'XDT'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 1}, + {:at => Time.utc(2000, 2, 1), :offset_index => 2}, + {:at => Time.utc(2000, 3, 1), :offset_index => 1}] + + # XDT will just assume an std_offset of +1 hour and calculate the utc_offset + # from utc_total_offset - std_offset. + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/AdjacentEqual', path) + assert_equal('Zone/AdjacentEqual', info.identifier) + + assert_period(:LMT, 7142, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period(:XDT, 3600, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XST, 7200, 0, false, Time.utc(2000, 3, 1), nil, info) + end + end + + def test_load_offset_utc_offset_preserved_from_next + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT1'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT2'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 1}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 4}, + {:at => Time.utc(2000, 4, 1), :offset_index => 2}] + + # Both XDT1 and XDT2 should both use the closest utc_offset (7200) (and not + # an equivalent utc_offset of 3600 and std_offset of 7200). + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path) + assert_equal('Zone/UtcOffsetPreserved', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period(:XDT1, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XDT2, 7200, 3600, true, Time.utc(2000, 3, 1), Time.utc(2000, 4, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 4, 1), nil, info) + end + end + + def test_load_offset_utc_offset_preserved_from_previous + # The zoneinfo files don't include the offset from standard time, so this + # has to be derived by looking at changes in the total UTC offset. + + offsets = [ + {:gmtoff => 3542, :isdst => false, :abbrev => 'LMT'}, + {:gmtoff => 3600, :isdst => false, :abbrev => 'XST1'}, + {:gmtoff => 7200, :isdst => false, :abbrev => 'XST2'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT1'}, + {:gmtoff => 10800, :isdst => true, :abbrev => 'XDT2'}] + + transitions = [ + {:at => Time.utc(2000, 1, 1), :offset_index => 2}, + {:at => Time.utc(2000, 2, 1), :offset_index => 3}, + {:at => Time.utc(2000, 3, 1), :offset_index => 4}, + {:at => Time.utc(2000, 4, 1), :offset_index => 1}] + + # Both XDT1 and XDT2 should both use the closest utc_offset (7200) (and not + # an equivalent utc_offset of 3600 and std_offset of 7200). + + tzif_test(offsets, transitions) do |path, format| + info = ZoneinfoTimezoneInfo.new('Zone/UtcOffsetPreserved', path) + assert_equal('Zone/UtcOffsetPreserved', info.identifier) + + assert_period( :LMT, 3542, 0, false, nil, Time.utc(2000, 1, 1), info) + assert_period(:XST2, 7200, 0, false, Time.utc(2000, 1, 1), Time.utc(2000, 2, 1), info) + assert_period(:XDT1, 7200, 3600, true, Time.utc(2000, 2, 1), Time.utc(2000, 3, 1), info) + assert_period(:XDT2, 7200, 3600, true, Time.utc(2000, 3, 1), Time.utc(2000, 4, 1), info) + assert_period(:XST1, 3600, 0, false, Time.utc(2000, 4, 1), nil, info) + end + end def test_load_in_safe_mode offsets = [{:gmtoff => -12094, :isdst => false, :abbrev => 'LT'}]