lib/rufus/sc/cronline.rb in rufus-scheduler-2.0.17 vs lib/rufus/sc/cronline.rb in rufus-scheduler-2.0.18
- old
+ new
@@ -1,7 +1,7 @@
#--
-# Copyright (c) 2006-2012, John Mettraux, jmettraux@gmail.com
+# Copyright (c) 2006-2013, John Mettraux, jmettraux@gmail.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
@@ -67,23 +67,30 @@
@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, @monthdays = parse_weekdays(items[4 + offset])
+
+ [ @seconds, @minutes, @hours, @months ].each do |es|
+
+ raise ArgumentError.new(
+ "invalid cronline: '#{line}'"
+ ) if es && es.find { |e| ! e.is_a?(Integer) }
+ end
end
# Returns true if the given time matches this cron line.
#
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, :sec, @seconds)
+ return false unless sub_match?(time, :min, @minutes)
+ return false unless sub_match?(time, :hour, @hours)
return false unless date_match?(time)
true
end
# Returns the next time that this cron line is supposed to 'fire'
@@ -124,19 +131,19 @@
unless date_match?(time)
time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
next
end
- unless sub_match?(time.hour, @hours)
+ unless sub_match?(time, :hour, @hours)
time += (60 - time.min) * 60 - time.sec
next
end
- unless sub_match?(time.min, @minutes)
+ unless sub_match?(time, :min, @minutes)
time += 60 - time.sec
next
end
- unless sub_match?(time.sec, @seconds)
+ unless sub_match?(time, :sec, @seconds)
time += 1
next
end
break
@@ -190,14 +197,18 @@
) if it.index('-')
(monthdays ||= []) << it
else
+ expr = it.dup
+ WEEKDAYS.each_with_index { |a, i| expr.gsub!(/#{a}/, i.to_s) }
- WEEKDAYS.each_with_index { |a, i| it.gsub!(/#{a}/, i.to_s) }
+ raise ArgumentError.new(
+ "invalid weekday expression (#{it})"
+ ) if expr !~ /^0*[0-7](-0*[0-7])?$/
- its = it.index('-') ? parse_range(it, 0, 7) : [ Integer(it) ]
+ its = expr.index('-') ? parse_range(expr, 0, 7) : [ Integer(expr) ]
its = its.collect { |i| i == 7 ? 0 : i }
(weekdays ||= []).concat(its)
end
end
@@ -208,12 +219,13 @@
end
def parse_item(item, min, max)
return nil if item == '*'
+ return [ 'L' ] if item == 'L'
return parse_list(item, min, max) if item.index(',')
- return parse_range(item, min, max) if item.index('*') or item.index('-')
+ return parse_range(item, min, max) if item.match(/[*-\/]/)
i = item.to_i
i = min if i < min
i = max if i > max
@@ -221,37 +233,41 @@
[ i ]
end
def parse_list(item, min, max)
- item.split(',').inject([]) { |r, i|
- r.push(parse_range(i, min, max))
- }.flatten
+ l = item.split(',').collect { |i| parse_range(i, min, max) }.flatten
+
+ raise ArgumentError.new(
+ "found duplicates in #{item.inspect}"
+ ) if l.uniq.size < l.size
+
+ l
end
def parse_range(item, min, max)
- i = item.index('-')
- j = item.index('/')
+ dash = item.index('-')
+ slash = item.index('/')
- return item.to_i if (not i and not j)
+ return parse_item(item, min, max) if (not slash) and (not dash)
- inc = j ? item[j + 1..-1].to_i : 1
+ raise ArgumentError.new(
+ "'L' (end of month) is not accepted in ranges, " +
+ "#{item.inspect} is not valid"
+ ) if item.index('L')
+ inc = slash ? item[slash + 1..-1].to_i : 1
+
istart = -1
iend = -1
- if i
+ if dash
- istart = item[0..i - 1].to_i
+ istart = item[0..dash - 1].to_i
+ iend = (slash ? item[dash + 1..slash - 1] : item[dash + 1..-1]).to_i
- iend = if j
- item[i + 1..j - 1].to_i
- else
- item[i + 1..-1].to_i
- end
-
else # case */x
istart = min
iend = max
end
@@ -269,25 +285,29 @@
end
result
end
- def sub_match?(value, values)
+ def sub_match?(time, accessor, values=:none)
- values.nil? || values.include?(value)
- end
+ value, values =
+ if values == :none
+ [ time, accessor ]
+ else
+ [ time.send(accessor), values ]
+ end
- def monthday_match(monthday, monthdays)
+ return true if values.nil?
+ return true if values.include?('L') && (time + 24 * 3600).day == 1
- return true if monthdays == nil
- return true if monthdays.include?(monthday)
+ 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)
+ return false unless sub_match?(date, :day, @days)
+ return false unless sub_match?(date, :month, @months)
+ return false unless sub_match?(date, :wday, @weekdays)
return false unless sub_match?(CronLine.monthday(date), @monthdays)
true
end
DAY_IN_SECONDS = 7 * 24 * 3600