lib/fugit/cron.rb in fugit-1.1.6 vs lib/fugit/cron.rb in fugit-1.1.7

- old
+ new

@@ -41,11 +41,11 @@ end def do_parse(s) parse(s) || - fail(ArgumentError.new("not a cron string #{s.inspect}")) + fail(ArgumentError.new("invalid cron string #{s.inspect}")) end end def to_cron_s @@ -164,31 +164,51 @@ 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 + def next_time(from=::EtOrbi::EoTime.now) from = ::EtOrbi.make_time(from) sfrom = from.strftime('%F/%T') + ifrom = from.to_i t = TimeCursor.new(from.translate(@timezone)) # # the translation occurs in the timezone of # this Fugit::Cron instance + ti = 0 + stalling = false + loop do - (from.to_i == t.to_i) && (t.inc(1); next) + ti1 = t.to_i + + fail RuntimeError.new( + "loop stalled for #{@original.inspect} #next_time, breaking" + ) if stalling && ti == ti1 + + 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) 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) st = t.time.strftime('%F/%T') - (from, sfrom = t.time, st; next) if st == sfrom + (from, sfrom, ifrom = t.time, st, t.time.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 @@ -201,15 +221,32 @@ 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 loop do -#p [ :l, Fugit.time_to_s(t.time) ] - (from.to_i == t.to_i) && (t.inc(-1); next) + + ti1 = t.to_i + + fail RuntimeError.new( + "loop stalled for #{@original.inspect} #previous_time, breaking" + ) if stalling && ti == ti1 + + 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) @@ -235,10 +272,11 @@ deltas = [] t = EtOrbi.make_time("#{year}-01-01") - 1 t0 = nil t1 = nil + loop do t1 = next_time(t) deltas << (t1 - t).to_i if t0 t0 ||= t1 break if deltas.any? && t1.year > year @@ -482,11 +520,11 @@ def slash(i); rex(:slash, i, /\/\d\d?/); end def core_mos(i); rex(:mos, i, /[0-5]?\d/); end # min or sec def core_hou(i); rex(:hou, i, /(2[0-4]|[01]?[0-9])/); end - def core_dom(i); rex(:dom, i, /(-?(3[01]|[012]?[0-9])|last|l)/i); end - def core_mon(i); rex(:mon, i, /(1[0-2]|0?[0-9]|#{MONTHS[1..-1].join('|')})/i); end + def core_dom(i); rex(:dom, i, /(-?(3[01]|[12][0-9]|0?[1-9])|last|l)/i); end + def core_mon(i); rex(:mon, i, /(1[0-2]|0?[1-9]|#{MONTHS[1..-1].join('|')})/i); end def core_dow(i); rex(:dow, i, /([0-7]|#{WEEKDS.join('|')})/i); end def dow_hash(i); rex(:hash, i, /#(-?[1-5]|last|l)/i); end def mos(i); core_mos(i); end