Skip to content

Commit

Permalink
Add support for named months and wdays
Browse files Browse the repository at this point in the history
  • Loading branch information
mizinsky committed Dec 22, 2023
1 parent 3894300 commit 73f4681
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 7 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ Calculates the last 'n' occurrences of the cron job until a given end time.\

## Unsupported features

- Named DOW and months (SUN-SAT, JAN-DEC)
- Joining characters , - /
- Predefined definitions (@yearly, @monthly, @weekly, @daily, @midnight, @hourly)

Expand Down
26 changes: 21 additions & 5 deletions lib/cron_calc.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative 'cron_calc/version'
require 'time'
require 'debug'

# The CronCalc is gem-wrapper module for the Parser class
module CronCalc
Expand All @@ -22,7 +23,18 @@ class Parser
hours: 0..23,
days: 1..31,
months: 1..12,
dows: 0..7
wdays: 0..6
}.freeze

WDAYS = {
'SUN' => '0', 'MON' => '1', 'TUE' => '2', 'WED' => '3',
'THU' => '4', 'FRI' => '5', 'SAT' => '6'
}.freeze

MONTHS = {
'JAN' => '1', 'FEB' => '2', 'MAR' => '3', 'APR' => '4',
'MAY' => '5', 'JUN' => '6', 'JUL' => '7', 'AUG' => '8',
'SEP' => '9', 'OCT' => '10', 'NOV' => '11', 'DEC' => '12'
}.freeze

def initialize(cron_string)
Expand Down Expand Up @@ -69,7 +81,7 @@ def last(count = 1, period_end = Time.now, max_years = 5)

def occurrences(period, count = nil, reverse: false)
time_combinations = generate_time_combinations(period, reverse).lazy
wdays = parse_cron_part(:dows)
wdays = parse_cron_part(:wdays)

time_combinations.each_with_object([]) do |(year, month, day, hour, minute), occ|
break occ if count && occ.length == count
Expand All @@ -94,8 +106,8 @@ def split_cron_string
minutes: splitted[0],
hours: splitted[1],
days: splitted[2],
months: splitted[3],
dows: splitted[4]
months: normalize_with(splitted[3], MONTHS),
wdays: normalize_with(splitted[4], WDAYS)
}
end

Expand Down Expand Up @@ -123,9 +135,13 @@ def parse_cron_part(time_unit)
end
# rubocop:enable Metrics

def normalize_with(string, mapping)
mapping.inject(string) { |str, (k, v)| str.gsub(k, v) }
end

def cron_string_valid?
# rubocop:disable Layout/LineLength
regex = %r{\A(\*|([0-5]?\d)(,([0-5]?\d))*|(\*/\d+)|(\d+-\d+)) (\*|([01]?\d|2[0-3])(,([01]?\d|2[0-3]))*|(\*/\d+)|(\d+-\d+)) (\*|([12]?\d|3[01])(,([12]?\d|3[01]))*|(\*/\d+)|(\d+-\d+)) (\*|([1-9]|1[0-2])(,([1-9]|1[0-2]))*|(\*/\d+)|(\d+-\d+)) (\*|([0-6])(,([0-6]))*|(\*/[0-6]+)|([0-6]-[0-6]))\z}
regex = %r{\A(\*|([0-5]?\d)(,([0-5]?\d))*|(\*/\d+)|(\d+-\d+)) (\*|([01]?\d|2[0-3])(,([01]?\d|2[0-3]))*|(\*/\d+)|(\d+-\d+)) (\*|([12]?\d|3[01])(,([12]?\d|3[01]))*|(\*/\d+)|(\d+-\d+)) (\*|(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC|[1-9]|1[0-2])(,(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC|[1-9]|1[0-2])|-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))*|(\*/\d+)|(\d+-\d+)) (\*|(SUN|MON|TUE|WED|THU|FRI|SAT|[0-6])(,(SUN|MON|TUE|WED|THU|FRI|SAT|[0-6])|-(SUN|MON|TUE|WED|THU|FRI|SAT))*|(\*/[0-6]+)|([0-6]-[0-6]))\z}
# rubocop:enable Layout/LineLength
cron_string.match?(regex)
end
Expand Down
28 changes: 27 additions & 1 deletion spec/cron_calc_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
end
end

context 'when DOW excludes days of month' do
context 'when wdays excludes days of month' do
let(:cron_string) { '5 5 14-22 * 0,6' }
let(:period) { Time.new(2024, 1, 1, 0, 0)..Time.new(2024, 2, 1, 0, 0) }

Expand All @@ -144,6 +144,32 @@
])
end
end

context 'when named months are used' do
let(:cron_string) { '5 5 5 JAN,FEB *' }
let(:period) { Time.new(2024, 1, 1, 0, 0)..Time.new(2024, 3, 31, 0, 0) }

it do
expect(subject).to eq([
Time.new(2024, 1, 5, 5, 5),
Time.new(2024, 2, 5, 5, 5)
])
end
end

context 'when named wdays are used' do
let(:cron_string) { '5 5 1-8 FEB TUE-THU' }
let(:period) { Time.new(2024, 1, 1, 0, 0)..Time.new(2024, 3, 31, 0, 0) }

it do
expect(subject).to eq([
Time.new(2024, 2, 1, 5, 5),
Time.new(2024, 2, 6, 5, 5),
Time.new(2024, 2, 7, 5, 5),
Time.new(2024, 2, 8, 5, 5)
])
end
end
end

describe '#next' do
Expand Down

0 comments on commit 73f4681

Please sign in to comment.