lib/rufus/sc/cronline.rb in rufus-scheduler-2.0.6 vs lib/rufus/sc/cronline.rb in rufus-scheduler-2.0.7

- old
+ new

@@ -20,82 +20,75 @@ # THE SOFTWARE. # # Made in Japan. #++ +require 'tzinfo' + module Rufus # # A 'cron line' is a line in the sense of a crontab # (man 5 crontab) file line. # class CronLine - # # The string used for creating this cronline instance. # attr_reader :original - attr_reader \ - :seconds, - :minutes, - :hours, - :days, - :months, - :weekdays + attr_reader :seconds + attr_reader :minutes + attr_reader :hours + attr_reader :days + attr_reader :months + attr_reader :weekdays + attr_reader :timezone - def initialize (line) + def initialize(line) super() @original = line items = line.split - unless items.length == 5 or items.length == 6 - raise( - "cron '#{line}' string should hold 5 or 6 items, not #{items.length}") - end + @timezone = (TZInfo::Timezone.get(items.last) rescue nil) + items.pop if @timezone + raise ArgumentError.new( + "not a valid cronline : '#{line}'" + ) unless items.length == 5 or items.length == 6 + offset = items.length - 5 @seconds = offset == 1 ? parse_item(items[0], 0, 59) : [ 0 ] @minutes = parse_item(items[0 + offset], 0, 59) @hours = parse_item(items[1 + offset], 0, 24) @days = parse_item(items[2 + offset], 1, 31) @months = parse_item(items[3 + offset], 1, 12) @weekdays = parse_weekdays(items[4 + offset]) end - # # Returns true if the given time matches this cron line. # - def matches? (time) + def matches?(time) time = Time.at(time) unless time.kind_of?(Time) + time = @timezone.utc_to_local(time.getutc) if @timezone + return false unless sub_match?(time.sec, @seconds) return false unless sub_match?(time.min, @minutes) return false unless sub_match?(time.hour, @hours) return false unless sub_match?(time.day, @days) return false unless sub_match?(time.month, @months) return false unless sub_match?(time.wday, @weekdays) true end - # - # Returns an array of 6 arrays (seconds, minutes, hours, days, - # months, weekdays). - # This method is used by the cronline unit tests. - # - def to_array - - [ @seconds, @minutes, @hours, @days, @months, @weekdays ] - end - - # # Returns the next time that this cron line is supposed to 'fire' # # This is raw, 3 secs to iterate over 1 year on my macbook :( brutal. # (Well, I was wrong, takes 0.001 sec on 1.8.7 and 1.9.1) # @@ -105,61 +98,84 @@ # Note that the time instance returned will be in the same time zone that # the given start point Time (thus a result in the local time zone will # be passed if no start time is specified (search start time set to # Time.now)) # - # >> Rufus::CronLine.new('30 7 * * *').next_time( Time.mktime(2008,10,24,7,29) ) - # => Fri Oct 24 07:30:00 -0500 2008 + # Rufus::CronLine.new('30 7 * * *').next_time( + # Time.mktime(2008, 10, 24, 7, 29)) + # #=> Fri Oct 24 07:30:00 -0500 2008 # - # >> Rufus::CronLine.new('30 7 * * *').next_time( Time.utc(2008,10,24,7,29) ) - # => Fri Oct 24 07:30:00 UTC 2008 + # Rufus::CronLine.new('30 7 * * *').next_time( + # Time.utc(2008, 10, 24, 7, 29)) + # #=> Fri Oct 24 07:30:00 UTC 2008 # - # >> Rufus::CronLine.new('30 7 * * *').next_time( Time.utc(2008,10,24,7,29) ).localtime - # => Fri Oct 24 02:30:00 -0500 2008 + # Rufus::CronLine.new('30 7 * * *').next_time( + # Time.utc(2008, 10, 24, 7, 29)).localtime + # #=> Fri Oct 24 02:30:00 -0500 2008 # # (Thanks to K Liu for the note and the examples) # - def next_time (time=Time.now) + def next_time(now=Time.now) - time -= time.usec * 1e-6 - time += 1 + time = @timezone ? @timezone.utc_to_local(now.getutc) : now + time = time - time.usec * 1e-6 + 1 + # little adjustment before starting + 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 end - unless sub_match?(time.min, @minutes) time += 60 - time.sec next end - unless sub_match?(time.sec, @seconds) time += 1 next end break end + if @timezone + time = @timezone.local_to_utc(time) + time = time.getlocal unless now.utc? + end + time end + # Returns an array of 6 arrays (seconds, minutes, hours, days, + # months, weekdays). + # This method is used by the cronline unit tests. + # + def to_array + + [ + @seconds, + @minutes, + @hours, + @days, + @months, + @weekdays, + @timezone ? @timezone.name : nil + ] + end + private WDS = %w[ sun mon tue wed thu fri sat ] - # # used by parse_weekday() - def parse_weekdays (item) + def parse_weekdays(item) item = item.downcase WDS.each_with_index { |day, index| item = item.gsub(day, index.to_s) } @@ -168,11 +184,11 @@ r.is_a?(Array) ? r.collect { |e| e == 7 ? 0 : e }.uniq : r end - def parse_item (item, min, max) + def parse_item(item, min, max) return nil if item == '*' return parse_list(item, min, max) if item.index(',') return parse_range(item, min, max) if item.index('*') or item.index('-') @@ -182,18 +198,18 @@ i = max if i > max [ i ] end - def parse_list (item, min, max) + def parse_list(item, min, max) item.split(',').inject([]) { |r, i| r.push(parse_range(i, min, max)) }.flatten end - def parse_range (item, min, max) + def parse_range(item, min, max) i = item.index('-') j = item.index('/') return item.to_i if (not i and not j) @@ -233,18 +249,19 @@ result end def sub_match?(value, values) + values.nil? || values.include?(value) end def date_match?(date) + return false unless sub_match?(date.day, @days) return false unless sub_match?(date.month, @months) return false unless sub_match?(date.wday, @weekdays) true end end - end