lib/rufus/scheduler/cronline.rb in rufus-scheduler-3.0.2 vs lib/rufus/scheduler/cronline.rb in rufus-scheduler-3.0.3

- old
+ new

@@ -120,20 +120,17 @@ # # (Thanks to K Liu for the note and the examples) # def next_time(from=Time.now) - time = @timezone ? @timezone.utc_to_local(from.getutc) : from + time = local_time(from) + time = round_to_seconds(time) - time = time.respond_to?(:round) ? time.round : time - time.usec * 1e-6 - # chop off subseconds (and yes, Ruby 1.8 doesn't have #round) - + # start at the next second time = time + 1 - # start at the next second loop do - unless date_match?(time) time += (24 - time.hour) * 3600 - time.min * 60 - time.sec; next end unless sub_match?(time, :hour, @hours) time += (60 - time.min) * 60 - time.sec; next @@ -146,38 +143,42 @@ end break end - if @timezone - time = @timezone.local_to_utc(time) - time = time.getlocal unless from.utc? - end - - time + global_time(time, from.utc?) end - # Returns the previous the cronline matched. It's like next_time, but + # Returns the previous time the cronline matched. It's like next_time, but # for the past. # def previous_time(from=Time.now) - # looks back by slices of two hours, - # - # finds for '* * * * sun', '* * 13 * *' and '0 12 13 * *' - # starting 1970, 1, 1 in 1.8 to 2 seconds (says Rspec) + time = local_time(from) + time = round_to_seconds(time) - start = current = from - 2 * 3600 - result = nil + # start at the previous second + time = time - 1 loop do - nex = next_time(current) - return (result ? result : previous_time(start)) if nex > from - result = current = nex + unless date_match?(time) + time -= time.hour * 3600 + time.min * 60 + time.sec + 1; next + end + unless sub_match?(time, :hour, @hours) + time -= time.min * 60 + time.sec + 1; next + end + unless sub_match?(time, :min, @minutes) + time -= time.sec + 1; next + end + unless sub_match?(time, :sec, @seconds) + time -= 1; next + end + + break end - # never reached + global_time(time, from.utc?) end # Returns an array of 6 arrays (seconds, minutes, hours, days, # months, weekdays). # This method is used by the cronline unit tests. @@ -197,10 +198,31 @@ end # Returns the shortest delta between two potential occurences of the # schedule described by this cronline. # + # . + # + # For a simple cronline like "*/5 * * * *", obviously the frequency is + # five minutes. Why does this method look at a whole year of #next_time ? + # + # Consider "* * * * sun#2,sun#3", the computed frequency is 1 week + # (the shortest delta is the one between the second sunday and the third + # sunday). This method takes no chance and runs next_time for the span + # of a whole year and keeps the shortest. + # + # Of course, this method can get VERY slow if you call on it a second- + # based cronline... + # + # Since it's a rarely used method, I haven't taken the time to make it + # smarter/faster. + # + # One obvious improvement would be to cache the result once computed... + # + # See https://github.com/jmettraux/rufus-scheduler/issues/89 + # for a discussion about this method. + # def frequency delta = 366 * DAY_S t0 = previous_time(Time.local(2000, 1, 1)) @@ -377,9 +399,27 @@ break if d.month != date.month neg = neg - 1 end [ "#{WEEKDAYS[date.wday]}##{pos}", "#{WEEKDAYS[date.wday]}##{neg}" ] + end + + def local_time(time) + time = @timezone ? @timezone.utc_to_local(time.getutc) : time + end + + def global_time(time, from_in_utc) + if @timezone + time = @timezone.local_to_utc(time) + time = time.getlocal unless from_in_utc + end + + time + end + + def round_to_seconds(time) + # Ruby 1.8 doesn't have #round + time.respond_to?(:round) ? time.round : time - time.usec * 1e-6 end end end