Skip to content

Commit

Permalink
Fix double DST occurrences (#404)
Browse files Browse the repository at this point in the history
Fixes #398
  • Loading branch information
avit committed Jul 10, 2017
2 parents fe4d1a9 + 0dae675 commit 38bb026
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 303 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,7 @@

## Current

* [BUGFIX] Fix double DST occurrences (#398)
* [BUGFIX] Realign first wday for monday-based weekly rules (#402)
* [BUGFIX] Fix weekly realignment for `spans: true` option (#402)

Expand Down
23 changes: 4 additions & 19 deletions lib/ice_cube/schedule.rb
Expand Up @@ -420,7 +420,7 @@ def reset
# Find all of the occurrences for the schedule between opening_time
# and closing_time
# Iteration is unrolled in pairs to skip duplicate times in end of DST
def enumerate_occurrences(opening_time, closing_time = nil, options = {}, &block)
def enumerate_occurrences(opening_time, closing_time = nil, options = {})
opening_time = TimeUtil.match_zone(opening_time, start_time)
closing_time = TimeUtil.match_zone(closing_time, start_time)
opening_time += start_time.subsec - opening_time.subsec rescue 0
Expand All @@ -435,18 +435,9 @@ def enumerate_occurrences(opening_time, closing_time = nil, options = {}, &block
break unless (t0 = next_time(t1, closing_time))
break if closing_time && t0 > closing_time
if (spans ? (t0.end_time > opening_time) : (t0 >= opening_time))
yielder << (block_given? ? block.call(t0) : t0)
yielder << (block_given? ? yield(t0) : t0)
end
break unless (t1 = next_time(t0 + 1, closing_time))
break if closing_time && t1 > closing_time
if TimeUtil.same_clock?(t0, t1) && recurrence_rules.any?(&:dst_adjust?)
wind_back_dst
next (t1 += 1)
end
if (spans ? (t1.end_time > opening_time) : (t1 >= opening_time))
yielder << (block_given? ? block.call(t1) : t1)
end
next (t1 += 1)
t1 = t0 + 1
end
end
end
Expand All @@ -462,7 +453,7 @@ def next_time(time, closing_time)
min_time
end
end
break nil unless min_time
break unless min_time
next (time = min_time + 1) if exception_time?(min_time)
break Occurrence.new(min_time, min_time + duration)
end
Expand Down Expand Up @@ -513,12 +504,6 @@ def recurrence_rules_with_implicit_start_occurrence
end
end

def wind_back_dst
recurrence_rules.each do |rule|
rule.skipped_for_dst
end
end

end

end
48 changes: 20 additions & 28 deletions lib/ice_cube/time_util.rb
Expand Up @@ -47,7 +47,7 @@ def self.match_zone(input_time, reference)
time.in_time_zone(reference.time_zone)
else
if reference.utc?
time.utc
time.getgm
elsif reference.zone
time.getlocal
else
Expand Down Expand Up @@ -260,27 +260,32 @@ class TimeWrapper

def initialize(time, dst_adjust = true)
@dst_adjust = dst_adjust
@time = time
@base = time
if dst_adjust
@time = Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec + time.subsec)
else
@time = time
end
end

# Get the wrapper time back
# Get the wrapped time back in its original zone & format
def to_time
@time
return @time unless @dst_adjust
parts = @time.year, @time.month, @time.day, @time.hour, @time.min, @time.sec + @time.subsec
TimeUtil.build_in_zone(parts, @base)
end

# DST-safely add an interval of time to the wrapped time
def add(type, val)
type = :day if type == :wday
adjust do
@time += case type
when :year then TimeUtil.days_in_n_years(@time, val) * ONE_DAY
when :month then TimeUtil.days_in_n_months(@time, val) * ONE_DAY
when :day then val * ONE_DAY
when :hour then val * ONE_HOUR
when :min then val * ONE_MINUTE
when :sec then val
end
end
@time += case type
when :year then TimeUtil.days_in_n_years(@time, val) * ONE_DAY
when :month then TimeUtil.days_in_n_months(@time, val) * ONE_DAY
when :day then val * ONE_DAY
when :hour then val * ONE_HOUR
when :min then val * ONE_MINUTE
when :sec then val
end
end

# Clear everything below a certain type
Expand All @@ -289,25 +294,12 @@ def clear_below(type)
type = :day if type == :wday
CLEAR_ORDER.each do |ptype|
break if ptype == type
adjust do
send(:"clear_#{ptype}")
end
send :"clear_#{ptype}"
end
end

private

def adjust(&block)
if @dst_adjust
off = @time.utc_offset
yield
diff = off - @time.utc_offset
@time += diff if diff != 0
else
yield
end
end

def clear_sec
@time.sec > 0 ? @time -= @time.sec : @time
end
Expand Down

0 comments on commit 38bb026

Please sign in to comment.