lib/fugit/cron.rb in fugit-1.1.8 vs lib/fugit/cron.rb in fugit-1.1.9
- old
+ new
@@ -62,11 +62,12 @@
end
end
class TimeCursor
- def initialize(t)
+ def initialize(cron, t)
+ @cron = cron
@t = t.is_a?(TimeCursor) ? t.time : t
@t.seconds = @t.seconds.to_i
end
def time; @t; end
@@ -86,33 +87,54 @@
m = @t.month + 1
if m == 13; m = 1; y += 1; end
@t = ::EtOrbi.make(y, m, @t.zone)
self
end
- def inc_day; inc((24 - @t.hour) * 3600 - @t.min * 60 - @t.sec); end
- def inc_hour; inc((60 - @t.min) * 60 - @t.sec); end
- def inc_min; inc(60 - @t.sec); end
- def inc_sec(seconds)
- if sec = seconds.find { |s| s > @t.sec }
+ def inc_day
+ inc((24 - @t.hour) * 3600 - @t.min * 60 - @t.sec)
+ end
+ def inc_hour
+ inc((60 - @t.min) * 60 - @t.sec)
+ end
+ def inc_min
+ inc(60 - @t.sec)
+ end
+
+ def inc_sec
+ if sec = @cron.seconds.find { |s| s > @t.sec }
inc(sec - @t.sec)
else
- inc(60 - @t.sec + seconds.first)
+ inc(60 - @t.sec + @cron.seconds.first)
end
end
def dec_month
- dec(@t.day * 24 * 3600 + @t.hour * 3600 + @t.min * 60 + @t.sec + 1)
+
+ #dec(@t.day * 24 * 3600 + @t.hour * 3600 + @t.min * 60 + @t.sec + 1)
+ #
+ # gh-18, so that '0 9 29 feb *' doesn't get skipped (over and over)
+ #
+ dec(@t.day * 24 * 3600 + 1)
end
- def dec_day; dec(@t.hour * 3600 + @t.min * 60 + @t.sec + 1); end
- def dec_hour; dec(@t.min * 60 + @t.sec + 1); end
- def dec_min; dec(@t.sec + 1); end
- def dec_sec(seconds)
- target = seconds.reverse.find { |s| s < @t.sec } || seconds.last
- inc(target - @t.sec)
+ def dec_day
+ dec(@t.hour * 3600 + @t.min * 60 + @t.sec + 1)
end
+ def dec_hour
+ dec(@t.min * 60 + @t.sec + 1)
+ end
+ def dec_min
+ dec(@t.sec + 1)
+ end
+
+ def dec_sec
+ target =
+ @cron.seconds.reverse.find { |s| s < @t.sec } ||
+ @cron.seconds.last
+ inc(target - @t.sec - (@t.sec > target ? 0 : 60))
+ end
end
def month_match?(nt); ( ! @months) || @months.include?(nt.month); end
def hour_match?(nt); ( ! @hours) || @hours.include?(nt.hour); end
def min_match?(nt); ( ! @minutes) || @minutes.include?(nt.min); end
@@ -138,11 +160,11 @@
def monthday_match?(nt)
return true if @monthdays.nil?
- last = (TimeCursor.new(nt).inc_month.time - 24 * 3600).day + 1
+ last = (TimeCursor.new(self, nt).inc_month.time - 24 * 3600).day + 1
@monthdays
.collect { |d| d < 1 ? last + d : d }
.include?(nt.day)
end
@@ -164,51 +186,47 @@
month_match?(t) && day_match?(t) &&
hour_match?(t) && min_match?(t) && sec_match?(t)
end
- BREAKER_S = 41 * (365 + 1) * 24 * 3600
- # 41 years and a few days... there wont'be a next or a previous time
+ MAX_ITERATION_COUNT = 2048
+ #
+ # See gh-15 and tst/iteration_count.rb
+ #
+ # Initially set to 1024 after seeing the worst case for #next_time
+ # at 167 iterations, I placed it at 2048 after experimenting with
+ # gh-18 and noticing some > 1024 for some experiments. 2048 should
+ # be ok.
def next_time(from=::EtOrbi::EoTime.now)
from = ::EtOrbi.make_time(from)
- sfrom = from.strftime('%F/%T')
+ sfrom = from.strftime('%F|%T')
ifrom = from.to_i
- t = TimeCursor.new(from.translate(@timezone))
+ i = 0
+ t = TimeCursor.new(self, from.translate(@timezone))
#
# the translation occurs in the timezone of
# this Fugit::Cron instance
- ti = 0
- stalling = false
-
loop do
- ti1 = t.to_i
-
fail RuntimeError.new(
- "loop stalled for #{@original.inspect} #next_time, breaking"
- ) if stalling && ti == ti1
+ "too many loops for #{@original.inspect} #next_time, breaking, " +
+ "please fill an issue at https://git.io/fjJC9"
+ ) if (i += 1) > MAX_ITERATION_COUNT
- stalling = (ti == ti1)
- ti = ti1
-
- fail RuntimeError.new(
- "too many loops for #{@original.inspect} #next_time, breaking"
- ) if (ti - ifrom).abs > BREAKER_S
-
- (ifrom == ti) && (t.inc(1); next)
+ (ifrom == t.to_i) && (t.inc(1); next)
month_match?(t) || (t.inc_month; next)
day_match?(t) || (t.inc_day; next)
hour_match?(t) || (t.inc_hour; next)
min_match?(t) || (t.inc_min; next)
- sec_match?(t) || (t.inc_sec(@seconds); next)
+ sec_match?(t) || (t.inc_sec; next)
- st = t.time.strftime('%F/%T')
- (from, sfrom, ifrom = t.time, st, t.time.to_i; next) if st == sfrom
+ st = t.time.strftime('%F|%T')
+ (from, sfrom, ifrom = t.time, st, t.to_i; next) if st == sfrom
#
# when transitioning out of DST, this prevents #next_time from
# yielding the same literal time twice in a row, see gh-6
break
@@ -221,36 +239,25 @@
end
def previous_time(from=::EtOrbi::EoTime.now)
from = ::EtOrbi.make_time(from)
- ti = 0
- ifrom = from.to_i
- t = TimeCursor.new(from.translate(@timezone))
- stalling = false
+ i = 0
+ t = TimeCursor.new(self, (from - 1).translate(@timezone))
loop do
- ti1 = t.to_i
-
fail RuntimeError.new(
- "loop stalled for #{@original.inspect} #previous_time, breaking"
- ) if stalling && ti == ti1
+ "too many loops for #{@original.inspect} #previous_time, breaking, " +
+ "please fill an issue at https://git.io/fjJCQ"
+ ) if (i += 1) > MAX_ITERATION_COUNT
- stalling = (ti == ti1)
- ti = ti1
-
- fail RuntimeError.new(
- "too many loops for #{@original.inspect} #previous_time, breaking"
- ) if (ifrom - ti).abs > BREAKER_S
-
- (ifrom == ti) && (t.inc(-1); next)
month_match?(t) || (t.dec_month; next)
day_match?(t) || (t.dec_day; next)
hour_match?(t) || (t.dec_hour; next)
min_match?(t) || (t.dec_min; next)
- sec_match?(t) || (t.dec_sec(@seconds); next)
+ sec_match?(t) || (t.dec_sec; next)
break
end
t.time.translate(from.zone)
end