cron_calc 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5757a8135782488cf246c22cd72ccc0aa04e00d06a726203df41205c0df79ef8
4
- data.tar.gz: d33394dd5136e58f41a4ea7d8e6759d5e51326e61bb9bc4303d9778d6f85de60
3
+ metadata.gz: 65bbd980a990785461ad8513755d5ef1effc8d14f4dac4205bf0381f4489bbcd
4
+ data.tar.gz: 116ac513ff00017a519997d1399748faa95284b2171e6f11c5097a25b10525ee
5
5
  SHA512:
6
- metadata.gz: 708988f704dee9a33f9331c3508485c2bcc45d7184fac956c590dad69e468e8eabb282a863d391f13f655c1816ef2aae62e77b8d66db755cec4122530ddde178
7
- data.tar.gz: 4dfc4c4c89ea7e983d95837dfb014992c2916b0cb54d731cb2f95441687cddf94bfeb90133cc180fddd4e3a96d23254819c8ec416db1498200270a67c291af5b
6
+ metadata.gz: 70d5d114af61a0430a52b8ef0fc06caecc693fe8f632472814f4335543b038ba6c94565069f831ee7486a8633e015e8833c65b6f1ba2814832a7bb5e4352c9fb
7
+ data.tar.gz: cb5b23e742052e1b76035721214c28c2a9ce162b70d7c1d60bb308e378db36318c3e4ceac9e65b4a7dc779c1cc710109880f8b921f00580df77bf5f1cc8d3539
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
- ## [Unreleased]
1
+ ## [0.3.0] - 2023-12-22
2
2
 
3
- ## [0.1.0] - 2023-11-27
3
+ - Added support for DOWs
4
+
5
+ ## [0.2.0] - 2023-12-19
6
+
7
+ - Added #next method
8
+ - Added #last method
9
+
10
+ ## [0.1.0] - 2023-12-18
4
11
 
5
12
  - Initial release
data/README.md CHANGED
@@ -4,25 +4,74 @@ CronCalc: A Ruby gem for calculating and analyzing scheduled CRON job occurrence
4
4
 
5
5
  ## Installation
6
6
 
7
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
7
+ Install the gem by executing:
8
8
 
9
- Install the gem and add to the application's Gemfile by executing:
9
+ $ gem install cron_calc
10
10
 
11
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
11
+ ## Usage
12
12
 
13
- If bundler is not being used to manage dependencies, install the gem by executing:
13
+ After installing `cron_calc` you can initialize `CronCalc` with the CRON string.
14
14
 
15
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
15
+ ```ruby
16
+ require 'cron_calc'
17
+ cron_calc = CronCalc.new('5 5 * * *')
18
+ ```
16
19
 
17
- ## Usage
20
+ Now, you can use one of three methods `#in`, `#next`, `#last` to determine cron job occurrencies within specified period.
21
+
22
+ ### Using `#in`
23
+
24
+ Calculates cron job occurrences within a given time period.\
25
+ @param period [Range] The time period for which to calculate cron job occurrences.\
26
+ @return [Array<Time>] An array of Time instances representing each occurrence.
27
+
28
+ ```ruby
29
+ period = Time.new(2024, 1, 1, 0, 0)..Time.new(2024, 1, 4, 0, 0)
30
+ cron_calc.in(period)
31
+
32
+ # => [2024-01-01 05:05:00 +0100, 2024-01-02 05:05:00 +0100, 2024-01-03 05:05:00 +0100]
33
+ ```
34
+
35
+ ### Using `#next`
36
+
37
+ Calculates the next 'n' occurrences of the cron job from a given start time.\
38
+ @param count [Integer] The number of occurrences to calculate. Defaults to `1`.\
39
+ @param period_start [Time] The start time from which to calculate occurrences. Defaults to `Time.now`.\
40
+ @param max_years [Integer] The maximum number of years to consider for the period. Defaults to `5`.\
41
+ @return [Array<Time>] An array of the next 'n' occurrences.
42
+
43
+ ```ruby
44
+ cron_calc.next
45
+ # => [2023-12-20 05:05:00 +0100]
46
+
47
+ cron_calc.next(3)
48
+ # => [2023-12-20 05:05:00 +0100, 2023-12-21 05:05:00 +0100, 2023-12-22 05:05:00 +0100]
49
+
50
+ cron_calc.next(2, Time.new(2024, 1, 1, 0, 0))
51
+ # => [2024-01-01 05:05:00 +0100, 2024-01-02 05:05:00 +0100]
52
+ ```
53
+
54
+ ### Using `#last`
55
+
56
+ Calculates the last 'n' occurrences of the cron job until a given end time.\
57
+ @param count [Integer] The number of past occurrences to calculate.\
58
+ @param period_end [Time] The end time until which to calculate occurrences.\
59
+ @param max_years [Integer] The maximum number of years to consider for the period.\
60
+ @return [Array<Time>] An array of the last 'n' occurrences.
18
61
 
19
- TODO: Write usage instructions here
62
+ ```ruby
63
+ cron_calc.last
64
+ # => [2023-12-19 05:05:00 +0100]
20
65
 
21
- ## Development
66
+ cron_calc.last(4, Time.new(2024, 1, 1, 0, 0))
67
+ # => [2023-12-31 05:05:00 +0100, 2023-12-30 05:05:00 +0100, 2023-12-29 05:05:00 +0100, 2023-12-28 05:05:00 +0100]
68
+ ```
22
69
 
23
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
70
+ ## Unsupported features
24
71
 
25
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
72
+ - Named DOW and months (SUN-SAT, JAN-DEC)
73
+ - Joining characters , - /
74
+ - Predefined definitions (@yearly, @monthly, @weekly, @daily, @midnight, @hourly)
26
75
 
27
76
  ## Contributing
28
77
 
data/cron_calc.gemspec CHANGED
@@ -8,8 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ['Jakub Miziński']
9
9
  spec.email = ['jakubmizinski@gmail.com']
10
10
 
11
- spec.summary = 'calculates cron job occurrences'
12
- spec.description = 'calculates cron job occurrences within a specified period'
11
+ spec.summary = 'Calculates cron job occurrences'
12
+ spec.description = 'Calculates cron job occurrences within a specified period
13
+ or determines next/last "n" occurrences based on a specified time.'
13
14
  spec.homepage = 'https://github.com/mizinsky/cron_calc'
14
15
  spec.license = 'MIT'
15
16
  spec.required_ruby_version = '>= 2.6.0'
@@ -30,10 +31,4 @@ Gem::Specification.new do |spec|
30
31
  spec.bindir = 'exe'
31
32
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
33
  spec.require_paths = ['lib']
33
-
34
- # Uncomment to register a new dependency of your gem
35
- spec.add_dependency 'rspec', '~> 3.12'
36
-
37
- # For more information and examples about making a new gem, check out our
38
- # guide at: https://bundler.io/guides/creating_gem.html
39
34
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CronCalc
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/cron_calc.rb CHANGED
@@ -7,41 +7,85 @@ require 'time'
7
7
  module CronCalc
8
8
  class Error < StandardError; end
9
9
 
10
- def self.new(cron_string, period)
11
- Parser.new(cron_string, period)
10
+ def self.new(cron_string)
11
+ Parser.new(cron_string)
12
12
  end
13
13
 
14
14
  # The Parser class provides functionality to parse and calculate occurrences
15
15
  # of cron jobs within a given time period. It interprets cron strings and
16
16
  # calculates when cron jobs will occur
17
17
  class Parser
18
- attr_reader :cron_string, :cron_parts, :period
18
+ attr_reader :cron_string, :cron_parts
19
19
 
20
20
  RANGE = {
21
21
  minutes: 0..59,
22
22
  hours: 0..23,
23
23
  days: 1..31,
24
- months: 1..12
24
+ months: 1..12,
25
+ dows: 0..7
25
26
  }.freeze
26
27
 
27
- def initialize(cron_string, period)
28
+ def initialize(cron_string)
28
29
  @cron_string = cron_string
29
- @period = period
30
+
31
+ raise 'Cron expression is not supported or invalid' unless cron_string_valid?
32
+
30
33
  @cron_parts = split_cron_string
31
34
  end
32
35
 
33
- def occurrences
34
- raise 'Cron expression is not supported or invalid' unless cron_string_valid?
36
+ # Calculates cron job occurrences within a given time period.
37
+ # @param period [Range] The time period for which to calculate cron job occurrences.
38
+ # @return [Array<Time>] An array of Time instances representing each occurrence.
39
+ def in(period)
40
+ occurrences(period)
41
+ end
42
+
43
+ # Calculates the next 'n' occurrences of the cron job from a given start time.
44
+ # @param count [Integer] The number of occurrences to calculate.
45
+ # @param period_start [Time] The start time from which to calculate occurrences.
46
+ # @param max_years [Integer] The maximum number of years to consider for the period.
47
+ # @return [Array<Time>] An array of the next 'n' occurrences.
48
+ def next(count = 1, period_start = Time.now, max_years = 5)
49
+ occurrences(
50
+ period_start..(period_start + (60 * 60 * 24 * 365 * max_years)),
51
+ count
52
+ )
53
+ end
35
54
 
36
- minutes, hours, days, months, years = parse_cron_expression
55
+ # Calculates the last 'n' occurrences of the cron job until a given end time.
56
+ # @param count [Integer] The number of past occurrences to calculate.
57
+ # @param period_end [Time] The end time until which to calculate occurrences.
58
+ # @param max_years [Integer] The maximum number of years to consider for the period.
59
+ # @return [Array<Time>] An array of the last 'n' occurrences.
60
+ def last(count = 1, period_end = Time.now, max_years = 5)
61
+ occurrences(
62
+ (period_end - (60 * 60 * 24 * 365 * max_years))..period_end,
63
+ count,
64
+ reverse: true
65
+ )
66
+ end
67
+
68
+ private
69
+
70
+ def occurrences(period, count = nil, reverse: false)
71
+ time_combinations = generate_time_combinations(period, reverse).lazy
72
+ wdays = parse_cron_part(:dows)
73
+
74
+ time_combinations.each_with_object([]) do |(year, month, day, hour, minute), occ|
75
+ break occ if count && occ.length == count
37
76
 
38
- years.product(months, days, hours, minutes).each_with_object([]) do |(year, month, day, hour, minute), occ|
39
77
  time = Time.new(year, month, day, hour, minute)
40
- occ << time if Date.valid_date?(year, month, day) && period.include?(time)
78
+ occ << time if Date.valid_date?(year, month, day) && period.include?(time) && wdays.include?(time.wday)
41
79
  end
42
80
  end
43
81
 
44
- private
82
+ def generate_time_combinations(period, reverse)
83
+ minutes, hours, days, months = parse_cron_expression
84
+ years = (period.min.year..period.max.year).to_a
85
+
86
+ combinations = years.product(months, days, hours, minutes)
87
+ reverse ? combinations.reverse : combinations
88
+ end
45
89
 
46
90
  def split_cron_string
47
91
  splitted = cron_string.split
@@ -50,12 +94,13 @@ module CronCalc
50
94
  minutes: splitted[0],
51
95
  hours: splitted[1],
52
96
  days: splitted[2],
53
- months: splitted[3]
97
+ months: splitted[3],
98
+ dows: splitted[4]
54
99
  }
55
100
  end
56
101
 
57
102
  def parse_cron_expression
58
- %i[minutes hours days months].map { |unit| parse_cron_part(unit) } << (period.min.year..period.max.year).to_a
103
+ %i[minutes hours days months].map { |unit| parse_cron_part(unit) }
59
104
  end
60
105
 
61
106
  # rubocop:disable Metrics
@@ -80,7 +125,7 @@ module CronCalc
80
125
 
81
126
  def cron_string_valid?
82
127
  # rubocop:disable Layout/LineLength
83
- 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+)) \*\z}
128
+ 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}
84
129
  # rubocop:enable Layout/LineLength
85
130
  cron_string.match?(regex)
86
131
  end
metadata CHANGED
@@ -1,30 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cron_calc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Miziński
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-18 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rspec
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '3.12'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '3.12'
27
- description: calculates cron job occurrences within a specified period
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: |-
14
+ Calculates cron job occurrences within a specified period
15
+ or determines next/last "n" occurrences based on a specified time.
28
16
  email:
29
17
  - jakubmizinski@gmail.com
30
18
  executables: []
@@ -64,8 +52,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
52
  - !ruby/object:Gem::Version
65
53
  version: '0'
66
54
  requirements: []
67
- rubygems_version: 3.4.22
55
+ rubygems_version: 3.5.1
68
56
  signing_key:
69
57
  specification_version: 4
70
- summary: calculates cron job occurrences
58
+ summary: Calculates cron job occurrences
71
59
  test_files: []