diff --git a/README.md b/README.md index d40d529..8641614 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,32 @@ next_time = cron_parser.next(Time.now) # Last occurrence most_recent_time = cron_parser.last(Time.now) + +# next 5 occurences +next_times = cron_parser.next(Time.now,5) + +# last 5 occurences +previous_times = cron_parser.last(Time.now,5) +``` + +## Strict Matching AKA Friday the 13th matching + +From the crontab manpage: +``` +If both fields are restricted (i.e., aren't '*'), the command will be run when either field matches the current time. For example, ``30 4 1,15 * 5'' would cause a command to be run at 4:30 am on the 1st and 15th of each month, plus every Friday. ``` +While this is the designed behavior, Some might not find it to be desireable or expected. If you want to match based on Day of Week AND Day of month, you can turn on strict matching with a parameter when you initialize. + +Let's take a look at the difference between the two modes. + +``` +# Normal behavior +CronParser.new('0 1 13 * 5', Time).next(Time.now, 4) +=> [Fri 25 Mar 2016, Fri 01 Apr 2016, Fri 08 Apr 2016, Wed 13 Apr 2016] + +# Strict Matching +CronParser.new('0 1 13 * 5', Time,true).next(Time.now, 4) +=> [Fri 13 May 2016, Fri 13 Jan 2017, Fri 13 Oct 2017, Fri, 13 Apr 2018] + +``` \ No newline at end of file diff --git a/lib/cron_parser.rb b/lib/cron_parser.rb index 41a8a5e..3d9a48c 100644 --- a/lib/cron_parser.rb +++ b/lib/cron_parser.rb @@ -15,7 +15,6 @@ def initialize(time,time_source = Time) @day = time.day @hour = time.hour @min = time.min - @time_source = time_source end @@ -51,8 +50,9 @@ def inspect "sat" => "6" } - def initialize(source,time_source = Time) + def initialize(source,time_source = Time, strict_match = false) @source = interpret_vixieisms(source) + set_strict_match(strict_match) @time_source = time_source validate_source end @@ -80,7 +80,6 @@ def interpret_vixieisms(spec) # returns the next occurence after the given date def next(now = @time_source.now, num = 1) t = InternalTime.new(now, @time_source) - unless time_specs[:month][0].include?(t.month) nudge_month(t) t.day = 0 @@ -91,7 +90,7 @@ def next(now = @time_source.now, num = 1) t.hour = -1 end - unless time_specs[:hour][0].include?(t.hour) + unless time_specs[:hour][0].include?(t.hour) nudge_hour(t) t.min = -1 end @@ -184,19 +183,24 @@ def interpolate_weekdays_without_cache(year, month) # Careful, if both DOW and DOM fields are non-wildcard, # then we only need to match *one* for cron to run the job: - if not (mday_field == '*' and wday_field == '*') - valid_mday = [] if mday_field == '*' - valid_wday = [] if wday_field == '*' + unless strict_match? + unless (mday_field == '*' and wday_field == '*') + valid_mday = [] if mday_field == '*' + valid_wday = [] if wday_field == '*' + end end # Careful: crontabs may use either 0 or 7 for Sunday: valid_wday << 0 if valid_wday.include?(7) result = [] while t.month == month - result << t.mday if valid_mday.include?(t.mday) || valid_wday.include?(t.wday) + if strict_match? + result << t.mday if (valid_mday.include?(t.mday) && valid_wday.include?(t.wday)) + else + result << t.mday if (valid_mday.include?(t.mday) || valid_wday.include?(t.wday)) + end t = t.succ end - [Set.new(result), result] end @@ -208,23 +212,32 @@ def nudge_month(t, dir = :next) spec = time_specs[:month][1] next_value = find_best_next(t.month, spec, dir) t.month = next_value || (dir == :next ? spec.first : spec.last) + if strict_match? + until date_valid?(t) + next_value = find_best_next(t.month, spec, dir) + t.month = next_value || (dir == :next ? spec.first : spec.last) + nudge_year(t, dir) if next_value.nil? + valid_days = interpolate_weekdays(t.year, t.month)[1] + t.day = dir == :next ? valid_days.first : valid_days.last + end + else + nudge_year(t, dir) if next_value.nil? + # we changed the month, so its likely that the date is incorrect now + valid_days = interpolate_weekdays(t.year, t.month)[1] + t.day = dir == :next ? valid_days.first : valid_days.last + end - nudge_year(t, dir) if next_value.nil? - - # we changed the month, so its likely that the date is incorrect now - valid_days = interpolate_weekdays(t.year, t.month)[1] - t.day = dir == :next ? valid_days.first : valid_days.last end def date_valid?(t, dir = :next) - interpolate_weekdays(t.year, t.month)[0].include?(t.day) + weekdays = interpolate_weekdays(t.year, t.month)[0] + weekdays.include?(t.day) end def nudge_date(t, dir = :next, can_nudge_month = true) spec = interpolate_weekdays(t.year, t.month)[1] next_value = find_best_next(t.day, spec, dir) t.day = next_value || (dir == :next ? spec.first : spec.last) - nudge_month(t, dir) if next_value.nil? && can_nudge_month end @@ -232,7 +245,6 @@ def nudge_hour(t, dir = :next) spec = time_specs[:hour][1] next_value = find_best_next(t.hour, spec, dir) t.hour = next_value || (dir == :next ? spec.first : spec.last) - nudge_date(t, dir) if next_value.nil? end @@ -240,7 +252,6 @@ def nudge_minute(t, dir = :next) spec = time_specs[:minute][1] next_value = find_best_next(t.min, spec, dir) t.min = next_value || (dir == :next ? spec.first : spec.last) - nudge_hour(t, dir) if next_value.nil? end @@ -267,10 +278,8 @@ def substitute_parse_symbols(str) def stepped_range(rng, step = 1) len = rng.last - rng.first - num = len.div(step) result = (0..num).map { |i| rng.first + step * i } - result.pop if result[-1] == rng.last and rng.exclude_end? result end @@ -295,4 +304,16 @@ def validate_source raise ArgumentError, 'not a valid cronline' end end + + def strict_match? + @strict_match + end + + def set_strict_match(strict_match = false) + @strict_match = false + if strict_match && [time_specs[:dom].last, time_specs[:dow].last].all?{|v| v != '*'} + @strict_match = true + end + end + end diff --git a/spec/cron_parser_spec.rb b/spec/cron_parser_spec.rb index 94f7ad0..6c884b8 100644 --- a/spec/cron_parser_spec.rb +++ b/spec/cron_parser_spec.rb @@ -24,147 +24,290 @@ def parse_date(str) end end -describe "CronParser#next" do - [ - ["* * * * *", "2011-08-15 12:00", "2011-08-15 12:01",1], - ["* * * * *", "2011-08-15 02:25", "2011-08-15 02:26",1], - ["* * * * *", "2011-08-15 02:59", "2011-08-15 03:00",1], - ["*/15 * * * *", "2011-08-15 02:02", "2011-08-15 02:15",1], - ["*/15,25 * * * *", "2011-08-15 02:15", "2011-08-15 02:25",1], - ["30 3,6,9 * * *", "2011-08-15 02:15", "2011-08-15 03:30",1], - ["30 9 * * *", "2011-08-15 10:15", "2011-08-16 09:30",1], - ["30 9 * * *", "2011-08-31 10:15", "2011-09-01 09:30",1], - ["30 9 * * *", "2011-09-30 10:15", "2011-10-01 09:30",1], - ["0 9 * * *", "2011-12-31 10:15", "2012-01-01 09:00",1], - ["* * 12 * *", "2010-04-15 10:15", "2010-05-12 00:00",1], - ["* * * * 1,3", "2010-04-15 10:15", "2010-04-19 00:00",1], - ["* * * * MON,WED", "2010-04-15 10:15", "2010-04-19 00:00",1], - ["0 0 1 1 *", "2010-04-15 10:15", "2011-01-01 00:00",1], - ["0 0 * * 1", "2011-08-01 00:00", "2011-08-08 00:00",1], - ["0 0 * * 1", "2011-07-25 00:00", "2011-08-01 00:00",1], - ["45 23 7 3 *", "2011-01-01 00:00", "2011-03-07 23:45",1], - ["0 0 1 jun *", "2013-05-14 11:20", "2013-06-01 00:00",1], - ["0 0 1 may,jul *", "2013-05-14 15:00", "2013-07-01 00:00",1], - ["0 0 1 MAY,JUL *", "2013-05-14 15:00", "2013-07-01 00:00",1], - ["40 5 * * *", "2014-02-01 15:56", "2014-02-02 05:40",1], - ["0 5 * * 1", "2014-02-01 15:56", "2014-02-03 05:00",1], - ["10 8 15 * *", "2014-02-01 15:56", "2014-02-15 08:10",1], - ["50 6 * * 1", "2014-02-01 15:56", "2014-02-03 06:50",1], - ["1 2 * apr mOn", "2014-02-01 15:56", "2014-04-07 02:01",1], - ["1 2 3 4 7", "2014-02-01 15:56", "2014-04-03 02:01",1], - ["1 2 3 4 7", "2014-04-04 15:56", "2014-04-06 02:01",1], - ["1-20/3 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], - ["1,2,3 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], - ["1-9,15-30 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], - ["1-9/3,15-30/4 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], - ["1 2 3 jan mon", "2014-02-01 15:56", "2015-01-03 02:01",1], - ["1 2 3 4 mON", "2014-02-01 15:56", "2014-04-03 02:01",1], - ["1 2 3 jan 5", "2014-02-01 15:56", "2015-01-02 02:01",1], - ["@yearly", "2014-02-01 15:56", "2015-01-01 00:00",1], - ["@annually", "2014-02-01 15:56", "2015-01-01 00:00",1], - ["@monthly", "2014-02-01 15:56", "2014-03-01 00:00",1], - ["@weekly", "2014-02-01 15:56", "2014-02-02 00:00",1], - ["@daily", "2014-02-01 15:56", "2014-02-02 00:00",1], - ["@midnight", "2014-02-01 15:56", "2014-02-02 00:00",1], - ["@hourly", "2014-02-01 15:56", "2014-02-01 16:00",1], - ["*/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:57",1], - ["0 5 * 2,3 *", "2014-02-01 15:56", "2014-02-02 05:00",1], - ["15-59/15 * * * *", "2014-02-01 15:56", "2014-02-01 16:15",1], - ["15-59/15 * * * *", "2014-02-01 15:00", "2014-02-01 15:15",1], - ["15-59/15 * * * *", "2014-02-01 15:01", "2014-02-01 15:15",1], - ["15-59/15 * * * *", "2014-02-01 15:16", "2014-02-01 15:30",1], - ["15-59/15 * * * *", "2014-02-01 15:26", "2014-02-01 15:30",1], - ["15-59/15 * * * *", "2014-02-01 15:36", "2014-02-01 15:45",1], - ["15-59/15 * * * *", "2014-02-01 15:45", "2014-02-01 16:15",4], - ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 16:15",3], - ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 16:15",2], - ].each do |line, now, expected_next,num| - it "returns #{expected_next} for '#{line}' when now is #{now}" do - parsed_now = parse_date(now) - expected = parse_date(expected_next) - parser = CronParser.new(line) - parser.next(parsed_now).xmlschema.should == expected.xmlschema - end - it "returns the expected class" do - parsed_now = parse_date(now) - expected = parse_date(expected_next) - parser = CronParser.new(line) - result = parser.next(parsed_now,num) - result.class.to_s.should == (num > 1 ? 'Array' : 'Time') +describe "Normal matching" do + describe "CronParser#next" do + [ + ["* * * * *", "2011-08-15 12:00", "2011-08-15 12:01",1], + ["* * * * *", "2011-08-15 02:25", "2011-08-15 02:26",1], + ["* * * * *", "2011-08-15 02:59", "2011-08-15 03:00",1], + ["*/15 * * * *", "2011-08-15 02:02", "2011-08-15 02:15",1], + ["*/15,25 * * * *", "2011-08-15 02:15", "2011-08-15 02:25",1], + ["30 3,6,9 * * *", "2011-08-15 02:15", "2011-08-15 03:30",1], + ["30 9 * * *", "2011-08-15 10:15", "2011-08-16 09:30",1], + ["30 9 * * *", "2011-08-31 10:15", "2011-09-01 09:30",1], + ["30 9 * * *", "2011-09-30 10:15", "2011-10-01 09:30",1], + ["0 9 * * *", "2011-12-31 10:15", "2012-01-01 09:00",1], + ["* * 12 * *", "2010-04-15 10:15", "2010-05-12 00:00",1], + ["* * * * 1,3", "2010-04-15 10:15", "2010-04-19 00:00",1], + ["* * * * MON,WED", "2010-04-15 10:15", "2010-04-19 00:00",1], + ["0 0 1 1 *", "2010-04-15 10:15", "2011-01-01 00:00",1], + ["0 0 * * 1", "2011-08-01 00:00", "2011-08-08 00:00",1], + ["0 0 * * 1", "2011-07-25 00:00", "2011-08-01 00:00",1], + ["45 23 7 3 *", "2011-01-01 00:00", "2011-03-07 23:45",1], + ["0 0 1 jun *", "2013-05-14 11:20", "2013-06-01 00:00",1], + ["0 0 1 may,jul *", "2013-05-14 15:00", "2013-07-01 00:00",1], + ["0 0 1 MAY,JUL *", "2013-05-14 15:00", "2013-07-01 00:00",1], + ["40 5 * * *", "2014-02-01 15:56", "2014-02-02 05:40",1], + ["0 5 * * 1", "2014-02-01 15:56", "2014-02-03 05:00",1], + ["10 8 15 * *", "2014-02-01 15:56", "2014-02-15 08:10",1], + ["50 6 * * 1", "2014-02-01 15:56", "2014-02-03 06:50",1], + ["1 2 * apr mOn", "2014-02-01 15:56", "2014-04-07 02:01",1], + ["1 2 3 4 7", "2014-02-01 15:56", "2014-04-03 02:01",1], + ["1 2 3 4 7", "2014-04-04 15:56", "2014-04-06 02:01",1], + ["1-20/3 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1,2,3 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1-9,15-30 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1-9/3,15-30/4 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1 2 3 jan mon", "2014-02-01 15:56", "2015-01-03 02:01",1], + ["1 2 3 4 mON", "2014-02-01 15:56", "2014-04-03 02:01",1], + ["1 2 3 jan 5", "2014-02-01 15:56", "2015-01-02 02:01",1], + ["@yearly", "2014-02-01 15:56", "2015-01-01 00:00",1], + ["@annually", "2014-02-01 15:56", "2015-01-01 00:00",1], + ["@monthly", "2014-02-01 15:56", "2014-03-01 00:00",1], + ["@weekly", "2014-02-01 15:56", "2014-02-02 00:00",1], + ["@daily", "2014-02-01 15:56", "2014-02-02 00:00",1], + ["@midnight", "2014-02-01 15:56", "2014-02-02 00:00",1], + ["@hourly", "2014-02-01 15:56", "2014-02-01 16:00",1], + ["*/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:57",1], + ["0 5 * 2,3 *", "2014-02-01 15:56", "2014-02-02 05:00",1], + ["15-59/15 * * * *", "2014-02-01 15:56", "2014-02-01 16:15",1], + ["15-59/15 * * * *", "2014-02-01 15:00", "2014-02-01 15:15",1], + ["15-59/15 * * * *", "2014-02-01 15:01", "2014-02-01 15:15",1], + ["15-59/15 * * * *", "2014-02-01 15:16", "2014-02-01 15:30",1], + ["15-59/15 * * * *", "2014-02-01 15:26", "2014-02-01 15:30",1], + ["15-59/15 * * * *", "2014-02-01 15:36", "2014-02-01 15:45",1], + ["15-59/15 * * * *", "2014-02-01 15:45", "2014-02-01 16:15",4], + ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 16:15",3], + ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 16:15",2], + ].each do |line, now, expected_next,num| + it "returns #{expected_next} for '#{line}' when now is #{now}" do + parsed_now = parse_date(now) + expected = parse_date(expected_next) + parser = CronParser.new(line) + parser.next(parsed_now).xmlschema.should == expected.xmlschema + end + it "returns the expected class" do + parsed_now = parse_date(now) + expected = parse_date(expected_next) + parser = CronParser.new(line) + result = parser.next(parsed_now,num) + result.class.to_s.should == (num > 1 ? 'Array' : 'Time') + end + it "returns the expected count" do + parsed_now = parse_date(now) + expected = parse_date(expected_next) + parser = CronParser.new(line) + result = parser.next(parsed_now,num) + if result.class.to_s == 'Array' + result.size.should == num + else + result.class.to_s.should == 'Time' + end + end end - it "returns the expected count" do - parsed_now = parse_date(now) - expected = parse_date(expected_next) - parser = CronParser.new(line) - result = parser.next(parsed_now,num) - if result.class.to_s == 'Array' - result.size.should == num - else - result.class.to_s.should == 'Time' + end + + describe "CronParser#last" do + [ + ["* * * * *", "2011-08-15 12:00", "2011-08-15 11:59"], + ["* * * * *", "2011-08-15 02:25", "2011-08-15 02:24"], + ["* * * * *", "2011-08-15 03:00", "2011-08-15 02:59"], + ["*/15 * * * *", "2011-08-15 02:02", "2011-08-15 02:00"], + ["*/15,45 * * * *", "2011-08-15 02:55", "2011-08-15 02:45"], + ["*/15,25 * * * *", "2011-08-15 02:35", "2011-08-15 02:30"], + ["30 3,6,9 * * *", "2011-08-15 02:15", "2011-08-14 09:30"], + ["30 9 * * *", "2011-08-15 10:15", "2011-08-15 09:30"], + ["30 9 * * *", "2011-09-01 08:15", "2011-08-31 09:30"], + ["30 9 * * *", "2011-10-01 08:15", "2011-09-30 09:30"], + ["0 9 * * *", "2012-01-01 00:15", "2011-12-31 09:00"], + ["* * 12 * *", "2010-04-15 10:15", "2010-04-12 23:59"], + ["* * * * 1,3", "2010-04-15 10:15", "2010-04-14 23:59"], + ["* * * * MON,WED", "2010-04-15 10:15", "2010-04-14 23:59"], + ["0 0 1 1 *", "2010-04-15 10:15", "2010-01-01 00:00"], + ["0 0 1 jun *", "2013-05-14 11:20", "2012-06-01 00:00"], + ["0 0 1 may,jul *", "2013-05-14 15:00", "2013-05-01 00:00"], + ["0 0 1 MAY,JUL *", "2013-05-14 15:00", "2013-05-01 00:00"], + ["40 5 * * *", "2014-02-01 15:56", "2014-02-01 05:40"], + ["0 5 * * 1", "2014-02-01 15:56", "2014-01-27 05:00"], + ["10 8 15 * *", "2014-02-01 15:56", "2014-01-15 08:10"], + ["50 6 * * 1", "2014-02-01 15:56", "2014-01-27 06:50"], + ["1 2 * apr mOn", "2014-02-01 15:56", "2013-04-29 02:01"], + ["1 2 3 4 7", "2014-02-01 15:56", "2013-04-28 02:01"], + ["1 2 3 4 7", "2014-04-04 15:56", "2014-04-03 02:01"], + ["1-20/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:19"], + ["1,2,3 * * * *", "2014-02-01 15:56", "2014-02-01 15:03"], + ["1-9,15-30 * * * *", "2014-02-01 15:56", "2014-02-01 15:30"], + ["1-9/3,15-30/4 * * * *", "2014-02-01 15:56", "2014-02-01 15:27"], + ["1 2 3 jan mon", "2014-02-01 15:56", "2014-01-27 02:01"], + ["1 2 3 4 mON", "2014-02-01 15:56", "2013-04-29 02:01"], + ["1 2 3 jan 5", "2014-02-01 15:56", "2014-01-31 02:01"], + ["@yearly", "2014-02-01 15:56", "2014-01-01 00:00"], + ["@annually", "2014-02-01 15:56", "2014-01-01 00:00"], + ["@monthly", "2014-02-01 15:56", "2014-02-01 00:00"], + ["@weekly", "2014-02-01 15:56", "2014-01-26 00:00"], + ["@daily", "2014-02-01 15:56", "2014-02-01 00:00"], + ["@midnight", "2014-02-01 15:56", "2014-02-01 00:00"], + ["@hourly", "2014-02-01 15:56", "2014-02-01 15:00"], + ["*/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:54"], + ["0 5 * 2,3 *", "2014-02-01 15:56", "2014-02-01 05:00"], + ["15-59/15 * * * *", "2014-02-01 15:56", "2014-02-01 15:45"], + ["15-59/15 * * * *", "2014-02-01 15:00", "2014-02-01 14:45"], + ["15-59/15 * * * *", "2014-02-01 15:01", "2014-02-01 14:45"], + ["15-59/15 * * * *", "2014-02-01 15:16", "2014-02-01 15:15"], + ["15-59/15 * * * *", "2014-02-01 15:26", "2014-02-01 15:15"], + ["15-59/15 * * * *", "2014-02-01 15:36", "2014-02-01 15:30"], + ["15-59/15 * * * *", "2014-02-01 15:45", "2014-02-01 15:30"], + ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 15:45"], + ].each do |line, now, expected_next| + it "should return #{expected_next} for '#{line}' when now is #{now}" do + now = parse_date(now) + expected_next = parse_date(expected_next) + parser = CronParser.new(line) + parser.last(now).should == expected_next end end end end -describe "CronParser#last" do - [ - ["* * * * *", "2011-08-15 12:00", "2011-08-15 11:59"], - ["* * * * *", "2011-08-15 02:25", "2011-08-15 02:24"], - ["* * * * *", "2011-08-15 03:00", "2011-08-15 02:59"], - ["*/15 * * * *", "2011-08-15 02:02", "2011-08-15 02:00"], - ["*/15,45 * * * *", "2011-08-15 02:55", "2011-08-15 02:45"], - ["*/15,25 * * * *", "2011-08-15 02:35", "2011-08-15 02:30"], - ["30 3,6,9 * * *", "2011-08-15 02:15", "2011-08-14 09:30"], - ["30 9 * * *", "2011-08-15 10:15", "2011-08-15 09:30"], - ["30 9 * * *", "2011-09-01 08:15", "2011-08-31 09:30"], - ["30 9 * * *", "2011-10-01 08:15", "2011-09-30 09:30"], - ["0 9 * * *", "2012-01-01 00:15", "2011-12-31 09:00"], - ["* * 12 * *", "2010-04-15 10:15", "2010-04-12 23:59"], - ["* * * * 1,3", "2010-04-15 10:15", "2010-04-14 23:59"], - ["* * * * MON,WED", "2010-04-15 10:15", "2010-04-14 23:59"], - ["0 0 1 1 *", "2010-04-15 10:15", "2010-01-01 00:00"], - ["0 0 1 jun *", "2013-05-14 11:20", "2012-06-01 00:00"], - ["0 0 1 may,jul *", "2013-05-14 15:00", "2013-05-01 00:00"], - ["0 0 1 MAY,JUL *", "2013-05-14 15:00", "2013-05-01 00:00"], - ["40 5 * * *", "2014-02-01 15:56", "2014-02-01 05:40"], - ["0 5 * * 1", "2014-02-01 15:56", "2014-01-27 05:00"], - ["10 8 15 * *", "2014-02-01 15:56", "2014-01-15 08:10"], - ["50 6 * * 1", "2014-02-01 15:56", "2014-01-27 06:50"], - ["1 2 * apr mOn", "2014-02-01 15:56", "2013-04-29 02:01"], - ["1 2 3 4 7", "2014-02-01 15:56", "2013-04-28 02:01"], - ["1 2 3 4 7", "2014-04-04 15:56", "2014-04-03 02:01"], - ["1-20/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:19"], - ["1,2,3 * * * *", "2014-02-01 15:56", "2014-02-01 15:03"], - ["1-9,15-30 * * * *", "2014-02-01 15:56", "2014-02-01 15:30"], - ["1-9/3,15-30/4 * * * *", "2014-02-01 15:56", "2014-02-01 15:27"], - ["1 2 3 jan mon", "2014-02-01 15:56", "2014-01-27 02:01"], - ["1 2 3 4 mON", "2014-02-01 15:56", "2013-04-29 02:01"], - ["1 2 3 jan 5", "2014-02-01 15:56", "2014-01-31 02:01"], - ["@yearly", "2014-02-01 15:56", "2014-01-01 00:00"], - ["@annually", "2014-02-01 15:56", "2014-01-01 00:00"], - ["@monthly", "2014-02-01 15:56", "2014-02-01 00:00"], - ["@weekly", "2014-02-01 15:56", "2014-01-26 00:00"], - ["@daily", "2014-02-01 15:56", "2014-02-01 00:00"], - ["@midnight", "2014-02-01 15:56", "2014-02-01 00:00"], - ["@hourly", "2014-02-01 15:56", "2014-02-01 15:00"], - ["*/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:54"], - ["0 5 * 2,3 *", "2014-02-01 15:56", "2014-02-01 05:00"], - ["15-59/15 * * * *", "2014-02-01 15:56", "2014-02-01 15:45"], - ["15-59/15 * * * *", "2014-02-01 15:00", "2014-02-01 14:45"], - ["15-59/15 * * * *", "2014-02-01 15:01", "2014-02-01 14:45"], - ["15-59/15 * * * *", "2014-02-01 15:16", "2014-02-01 15:15"], - ["15-59/15 * * * *", "2014-02-01 15:26", "2014-02-01 15:15"], - ["15-59/15 * * * *", "2014-02-01 15:36", "2014-02-01 15:30"], - ["15-59/15 * * * *", "2014-02-01 15:45", "2014-02-01 15:30"], - ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 15:45"], - ].each do |line, now, expected_next| - it "should return #{expected_next} for '#{line}' when now is #{now}" do - now = parse_date(now) - expected_next = parse_date(expected_next) - - parser = CronParser.new(line) +describe "Strict matching" do + describe "CronParser#next" do + [ + ["* * * * *", "2011-08-15 12:00", "2011-08-15 12:01",1], + ["* * * * *", "2011-08-15 02:25", "2011-08-15 02:26",1], + ["* * * * *", "2011-08-15 02:59", "2011-08-15 03:00",1], + ["*/15 * * * *", "2011-08-15 02:02", "2011-08-15 02:15",1], + ["*/15,25 * * * *", "2011-08-15 02:15", "2011-08-15 02:25",1], + ["30 3,6,9 * * *", "2011-08-15 02:15", "2011-08-15 03:30",1], + ["30 9 * * *", "2011-08-15 10:15", "2011-08-16 09:30",1], + ["30 9 * * *", "2011-08-31 10:15", "2011-09-01 09:30",1], + ["30 9 * * *", "2011-09-30 10:15", "2011-10-01 09:30",1], + ["0 9 * * *", "2011-12-31 10:15", "2012-01-01 09:00",1], + ["* * 12 * *", "2010-04-15 10:15", "2010-05-12 00:00",1], + ["* * * * 1,3", "2010-04-15 10:15", "2010-04-19 00:00",1], + ["* * * * MON,WED", "2010-04-15 10:15", "2010-04-19 00:00",1], + ["0 0 1 1 *", "2010-04-15 10:15", "2011-01-01 00:00",1], + ["0 0 * * 1", "2011-08-01 00:00", "2011-08-08 00:00",1], + ["0 0 * * 1", "2011-07-25 00:00", "2011-08-01 00:00",1], + ["45 23 7 3 *", "2011-01-01 00:00", "2011-03-07 23:45",1], + ["0 0 1 jun *", "2013-05-14 11:20", "2013-06-01 00:00",1], + ["0 0 1 may,jul *", "2013-05-14 15:00", "2013-07-01 00:00",1], + ["0 0 1 MAY,JUL *", "2013-05-14 15:00", "2013-07-01 00:00",1], + ["40 5 * * *", "2014-02-01 15:56", "2014-02-02 05:40",1], + ["0 5 * * 1", "2014-02-01 15:56", "2014-02-03 05:00",1], + ["10 8 15 * *", "2014-02-01 15:56", "2014-02-15 08:10",1], + ["50 6 * * 1", "2014-02-01 15:56", "2014-02-03 06:50",1], + ["1 2 * apr mOn", "2014-02-01 15:56", "2014-04-07 02:01",1], + ["1 2 3 4 7", "2014-02-01 15:56", "2016-04-03 02:01",1], + ["1-20/3 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1,2,3 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1-9,15-30 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1-9/3,15-30/4 * * * *", "2014-02-01 15:56", "2014-02-01 16:01",1], + ["1 2 3 jan mon", "2014-02-01 15:56", "2022-01-03 02:01",1], + ["1 2 3 4 mON", "2014-02-01 15:56", "2017-04-03 02:01",1], + ["1 2 3 jan 5", "2014-02-01 15:56", "2020-01-03 02:01",1], + ["@yearly", "2014-02-01 15:56", "2015-01-01 00:00",1], + ["@annually", "2014-02-01 15:56", "2015-01-01 00:00",1], + ["@monthly", "2014-02-01 15:56", "2014-03-01 00:00",1], + ["@weekly", "2014-02-01 15:56", "2014-02-02 00:00",1], + ["@daily", "2014-02-01 15:56", "2014-02-02 00:00",1], + ["@midnight", "2014-02-01 15:56", "2014-02-02 00:00",1], + ["@hourly", "2014-02-01 15:56", "2014-02-01 16:00",1], + ["*/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:57",1], + ["0 5 * 2,3 *", "2014-02-01 15:56", "2014-02-02 05:00",1], + ["15-59/15 * * * *", "2014-02-01 15:56", "2014-02-01 16:15",1], + ["15-59/15 * * * *", "2014-02-01 15:00", "2014-02-01 15:15",1], + ["15-59/15 * * * *", "2014-02-01 15:01", "2014-02-01 15:15",1], + ["15-59/15 * * * *", "2014-02-01 15:16", "2014-02-01 15:30",1], + ["15-59/15 * * * *", "2014-02-01 15:26", "2014-02-01 15:30",1], + ["15-59/15 * * * *", "2014-02-01 15:36", "2014-02-01 15:45",1], + ["15-59/15 * * * *", "2014-02-01 15:45", "2014-02-01 16:15",4], + ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 16:15",3], + ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 16:15",2], + ].each do |line, now, expected_next,num| + it "returns #{expected_next} for '#{line}' when now is #{now}" do + parsed_now = parse_date(now) + expected = parse_date(expected_next) + parser = CronParser.new(line,Time,true) + parser.next(parsed_now).xmlschema.should == expected.xmlschema + end + it "returns the expected class" do + parsed_now = parse_date(now) + expected = parse_date(expected_next) + parser = CronParser.new(line,Time,true) + result = parser.next(parsed_now,num) + result.class.to_s.should == (num > 1 ? 'Array' : 'Time') + end + it "returns the expected count" do + parsed_now = parse_date(now) + expected = parse_date(expected_next) + parser = CronParser.new(line,Time,true) + result = parser.next(parsed_now,num) + if result.class.to_s == 'Array' + result.size.should == num + else + result.class.to_s.should == 'Time' + end + end + end + end - parser.last(now).should == expected_next + describe "CronParser#last" do + [ + ["* * * * *", "2011-08-15 12:00", "2011-08-15 11:59"], + ["* * * * *", "2011-08-15 02:25", "2011-08-15 02:24"], + ["* * * * *", "2011-08-15 03:00", "2011-08-15 02:59"], + ["*/15 * * * *", "2011-08-15 02:02", "2011-08-15 02:00"], + ["*/15,45 * * * *", "2011-08-15 02:55", "2011-08-15 02:45"], + ["*/15,25 * * * *", "2011-08-15 02:35", "2011-08-15 02:30"], + ["30 3,6,9 * * *", "2011-08-15 02:15", "2011-08-14 09:30"], + ["30 9 * * *", "2011-08-15 10:15", "2011-08-15 09:30"], + ["30 9 * * *", "2011-09-01 08:15", "2011-08-31 09:30"], + ["30 9 * * *", "2011-10-01 08:15", "2011-09-30 09:30"], + ["0 9 * * *", "2012-01-01 00:15", "2011-12-31 09:00"], + ["* * 12 * *", "2010-04-15 10:15", "2010-04-12 23:59"], + ["* * * * 1,3", "2010-04-15 10:15", "2010-04-14 23:59"], + ["* * * * MON,WED", "2010-04-15 10:15", "2010-04-14 23:59"], + ["0 0 1 1 *", "2010-04-15 10:15", "2010-01-01 00:00"], + ["0 0 1 jun *", "2013-05-14 11:20", "2012-06-01 00:00"], + ["0 0 1 may,jul *", "2013-05-14 15:00", "2013-05-01 00:00"], + ["0 0 1 MAY,JUL *", "2013-05-14 15:00", "2013-05-01 00:00"], + ["40 5 * * *", "2014-02-01 15:56", "2014-02-01 05:40"], + ["0 5 * * 1", "2014-02-01 15:56", "2014-01-27 05:00"], + ["10 8 15 * *", "2014-02-01 15:56", "2014-01-15 08:10"], + ["50 6 * * 1", "2014-02-01 15:56", "2014-01-27 06:50"], + ["1 2 * apr mOn", "2014-02-01 15:56", "2013-04-29 02:01"], + ["1 2 3 4 7", "2014-02-01 15:56", "2011-04-03 02:01"], + ["1-20/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:19"], + ["1,2,3 * * * *", "2014-02-01 15:56", "2014-02-01 15:03"], + ["1-9,15-30 * * * *", "2014-02-01 15:56", "2014-02-01 15:30"], + ["1-9/3,15-30/4 * * * *", "2014-02-01 15:56", "2014-02-01 15:27"], + ["1 2 3 jan mon", "2014-02-01 15:56", "2011-01-03 02:01"], + ["1 2 3 4 mON", "2014-02-01 15:56", "2006-04-03 02:01"], + ["1 2 3 jan 5", "2014-02-01 15:56", "2003-01-03 02:01"], + ["@yearly", "2014-02-01 15:56", "2014-01-01 00:00"], + ["@annually", "2014-02-01 15:56", "2014-01-01 00:00"], + ["@monthly", "2014-02-01 15:56", "2014-02-01 00:00"], + ["@weekly", "2014-02-01 15:56", "2014-01-26 00:00"], + ["@daily", "2014-02-01 15:56", "2014-02-01 00:00"], + ["@midnight", "2014-02-01 15:56", "2014-02-01 00:00"], + ["@hourly", "2014-02-01 15:56", "2014-02-01 15:00"], + ["*/3 * * * *", "2014-02-01 15:56", "2014-02-01 15:54"], + ["0 5 * 2,3 *", "2014-02-01 15:56", "2014-02-01 05:00"], + ["15-59/15 * * * *", "2014-02-01 15:56", "2014-02-01 15:45"], + ["15-59/15 * * * *", "2014-02-01 15:00", "2014-02-01 14:45"], + ["15-59/15 * * * *", "2014-02-01 15:01", "2014-02-01 14:45"], + ["15-59/15 * * * *", "2014-02-01 15:16", "2014-02-01 15:15"], + ["15-59/15 * * * *", "2014-02-01 15:26", "2014-02-01 15:15"], + ["15-59/15 * * * *", "2014-02-01 15:36", "2014-02-01 15:30"], + ["15-59/15 * * * *", "2014-02-01 15:45", "2014-02-01 15:30"], + ["15-59/15 * * * *", "2014-02-01 15:46", "2014-02-01 15:45"], + ].each do |line, now, expected_next| + it "should return #{expected_next} for '#{line}' when now is #{now}" do + now = parse_date(now) + expected_next = parse_date(expected_next) + parser = CronParser.new(line,Time,true) + parser.last(now).should == expected_next + end end end end