lib/rufus/scheduler/cronline.rb in rufus-scheduler-3.2.0 vs lib/rufus/scheduler/cronline.rb in rufus-scheduler-3.2.1

- old
+ new

@@ -1,7 +1,7 @@ #-- -# Copyright (c) 2006-2015, John Mettraux, jmettraux@gmail.com +# Copyright (c) 2006-2016, 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 @@ -65,11 +65,11 @@ 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) + @days = parse_item(items[2 + offset], -30, 31) @months = parse_item(items[3 + offset], 1, 12) @weekdays, @monthdays = parse_weekdays(items[4 + offset]) [ @seconds, @minutes, @hours, @months ].each do |es| @@ -186,13 +186,13 @@ time end # Returns an array of 6 arrays (seconds, minutes, hours, days, # months, weekdays). - # This method is used by the cronline unit tests. + # This method is mostly used by the cronline specs. # - def to_array + def to_a [ toa(@seconds), toa(@minutes), toa(@hours), @@ -201,10 +201,11 @@ toa(@weekdays), toa(@monthdays), @timezone ] end + alias to_array to_a # Returns a quickly computed approximation of the frequency for this # cron line. # # #brute_frequency, on the other hand, will compute the frequency by @@ -317,37 +318,37 @@ def parse_weekdays(item) return nil if item == '*' - items = item.downcase.split(',') - weekdays = nil monthdays = nil - items.each do |it| + item.downcase.split(',').each do |it| - if m = it.match(/^(.+)#(l|-?[12345])$/) + WEEKDAYS.each_with_index { |a, i| it.gsub!(/#{a}/, i.to_s) } + it = it.gsub(/([^#])l/, '\1#-1') + # "5L" == "5#-1" == the last Friday + + if m = it.match(/\A(.+)#(l|-?[12345])\z/) + fail ArgumentError.new( "ranges are not supported for monthdays (#{it})" ) if m[1].index('-') - expr = it.gsub(/#l/, '#-1') + it = it.gsub(/#l/, '#-1') - (monthdays ||= []) << expr + (monthdays ||= []) << it else - expr = it.dup - WEEKDAYS.each_with_index { |a, i| expr.gsub!(/#{a}/, i.to_s) } - fail ArgumentError.new( - "invalid weekday expression (#{it})" - ) if expr !~ /^0*[0-7](-0*[0-7])?$/ + "invalid weekday expression (#{item})" + ) if it !~ /\A0*[0-7](-0*[0-7])?\z/ - its = expr.index('-') ? parse_range(expr, 0, 7) : [ Integer(expr) ] + its = it.index('-') ? parse_range(it, 0, 7) : [ Integer(it) ] its = its.collect { |i| i == 7 ? 0 : i } (weekdays ||= []).concat(its) end end @@ -370,11 +371,11 @@ r = sc_sort(r) Set.new(r) end - RANGE_REGEX = /^(\*|\d{1,2})(?:-(\d{1,2}))?(?:\/(\d{1,2}))?$/ + RANGE_REGEX = /\A(\*|-?\d{1,2})(?:-(-?\d{1,2}))?(?:\/(\d{1,2}))?\z/ def parse_range(item, min, max) return %w[ L ] if item == 'L' @@ -384,30 +385,44 @@ fail ArgumentError.new( "cannot parse #{item.inspect}" ) unless m + mmin = min == -30 ? 1 : min # days + sta = m[1] - sta = sta == '*' ? min : sta.to_i + sta = sta == '*' ? mmin : sta.to_i edn = m[2] edn = edn ? edn.to_i : sta edn = max if m[1] == '*' inc = m[3] inc = inc ? inc.to_i : 1 fail ArgumentError.new( + "#{item.inspect} positive/negative ranges not allowed" + ) if (sta < 0 && edn > 0) || (sta > 0 && edn < 0) + + fail ArgumentError.new( + "#{item.inspect} descending day ranges not allowed" + ) if min == -30 && sta > edn + + fail ArgumentError.new( "#{item.inspect} is not in range #{min}..#{max}" ) if sta < min || edn > max + fail ArgumentError.new( + "#{item.inspect} increment must be greater than zero" + ) if inc == 0 + r = [] val = sta loop do v = val - v = 0 if max == 24 && v == 24 + v = 0 if max == 24 && v == 24 # hours r << v break if inc == 1 && val == edn val += inc break if inc > 1 && val > edn val = min if val > max @@ -419,14 +434,24 @@ def sub_match?(time, accessor, values) value = time.send(accessor) return true if values.nil? - return true if values.include?('L') && (time + DAY_S).day == 1 - return true if value == 0 && accessor == :hour && values.include?(24) + if accessor == :day + values.each do |v| + return true if v == 'L' && (time + DAY_S).day == 1 + return true if v.to_i < 0 && (time + (1 - v) * DAY_S).day == 1 + end + end + + if accessor == :hour + + return true if value == 0 && values.include?(24) + end + values.include?(value) end def monthday_match?(date, values) @@ -464,10 +489,10 @@ d = d + WEEK_S break if d.month != date.month neg = neg - 1 end - [ "#{WEEKDAYS[date.wday]}##{pos}", "#{WEEKDAYS[date.wday]}##{neg}" ] + [ "#{date.wday}##{pos}", "#{date.wday}##{neg}" ] end end end