cron_calc 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -1
- data/README.md +33 -17
- data/lib/cron_calc/version.rb +1 -1
- data/lib/cron_calc.rb +56 -27
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aebec718fd55bb74df49580abe4309a41ad0246b2c266a143f9dfac807cd436e
|
4
|
+
data.tar.gz: 1b47dea7541f369800f1f816fdf2160e0721de4a4f8bcdd9434c0b77fdf85876
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6f3349709b4a5babb3d2267f7e8cf1a7e2e51d9a118044e67743cc02b31d34bf5d637b068e825afe9f10c24e34c97a906730363a2c1c309eb003408b2c48586
|
7
|
+
data.tar.gz: fd80e141fd67e56adb122b6acf2dbbeff5572e69868212c749d1f6d67c422d70790a9910cec991a2ef328841f8fea17e2214c6276761124f5e4630f04c613557
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,16 @@
|
|
1
|
+
## [1.0.0] - 2023-12-28
|
2
|
+
|
3
|
+
- Add support for predefined definitions
|
4
|
+
- Add support for joining ,-
|
5
|
+
|
6
|
+
## [0.4.0] - 2023-12-22
|
7
|
+
|
8
|
+
- Added support for named months and wdays
|
9
|
+
- Keyword arguments: before:, after:
|
10
|
+
|
1
11
|
## [0.3.0] - 2023-12-22
|
2
12
|
|
3
|
-
- Added support for DOWs
|
13
|
+
- Added support for DOWs (wdays)
|
4
14
|
|
5
15
|
## [0.2.0] - 2023-12-19
|
6
16
|
|
data/README.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# CronCalc
|
2
2
|
|
3
|
-
CronCalc
|
3
|
+
**CronCalc**: A Ruby gem for calculating CRON job occurrences. With this gem, you can easily determine when a cron job will occur by providing a cron expression. Key features include the ability to **calculate occurrences**:
|
4
|
+
- **Within a specified period**: Find out all the times your cron job will run during a particular timeframe.
|
5
|
+
- **After a given date**: Determine the next set of occurrences after a specific starting point.
|
6
|
+
- **Before a given date**: Discover when your cron job ran or would have run before a certain date.
|
7
|
+
|
8
|
+
This tool can be used for scheduling, forecasting, and analyzing tasks in systems that use cron for job scheduling.
|
4
9
|
|
5
10
|
## Installation
|
6
11
|
|
@@ -22,8 +27,9 @@ Now, you can use one of three methods `#in`, `#next`, `#last` to determine cron
|
|
22
27
|
### Using `#in`
|
23
28
|
|
24
29
|
Calculates cron job occurrences within a given time period.\
|
25
|
-
|
26
|
-
|
30
|
+
**Parameters:**
|
31
|
+
- `period` - a Range object defining the start and end times for the calculation.\
|
32
|
+
|
27
33
|
|
28
34
|
```ruby
|
29
35
|
period = Time.new(2024, 1, 1, 0, 0)..Time.new(2024, 1, 4, 0, 0)
|
@@ -35,10 +41,10 @@ Calculates cron job occurrences within a given time period.\
|
|
35
41
|
### Using `#next`
|
36
42
|
|
37
43
|
Calculates the next 'n' occurrences of the cron job from a given start time.\
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
**Parameters:**
|
45
|
+
- `count` - (optional, Integer) The number of occurrences to calculate. Defaults to 1.
|
46
|
+
- `after:` - (optional, Time, keyword argument) The start time from which to calculate occurrences. If not provided, defaults to the current time (Time.now).
|
47
|
+
- `max_years` - (optional, Integer, keyword argument) The maximum number of years to search for future occurrences. Defaults to 5.
|
42
48
|
|
43
49
|
```ruby
|
44
50
|
cron_calc.next
|
@@ -47,31 +53,41 @@ Calculates the next 'n' occurrences of the cron job from a given start time.\
|
|
47
53
|
cron_calc.next(3)
|
48
54
|
# => [2023-12-20 05:05:00 +0100, 2023-12-21 05:05:00 +0100, 2023-12-22 05:05:00 +0100]
|
49
55
|
|
50
|
-
cron_calc.next(2, Time.new(2024, 1, 1, 0, 0))
|
56
|
+
cron_calc.next(2, after: Time.new(2024, 1, 1, 0, 0))
|
51
57
|
# => [2024-01-01 05:05:00 +0100, 2024-01-02 05:05:00 +0100]
|
52
58
|
```
|
53
59
|
|
54
60
|
### Using `#last`
|
55
61
|
|
56
62
|
Calculates the last 'n' occurrences of the cron job until a given end time.\
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
**Parameters:**
|
64
|
+
- `count` - (optional, Integer) The number of occurrences to calculate. Defaults to 1.
|
65
|
+
- `before:` - (optional, Time, keyword argument) The end time from which to calculate past occurrences. If not provided, defaults to the current time (Time.now).
|
66
|
+
- `max_years` - (optional, Integer, keyword argument) The maximum number of years to search backward for past occurrences. Defaults to 5.
|
61
67
|
|
62
68
|
```ruby
|
63
69
|
cron_calc.last
|
64
70
|
# => [2023-12-19 05:05:00 +0100]
|
65
71
|
|
66
|
-
cron_calc.last(4, Time.new(2024, 1, 1, 0, 0))
|
72
|
+
cron_calc.last(4, before: Time.new(2024, 1, 1, 0, 0))
|
67
73
|
# => [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
74
|
```
|
69
75
|
|
70
|
-
|
76
|
+
### Other examples
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
# You can omit the count parameter
|
80
|
+
CronCalc.new('5 5 */5 * SUN').last(before: Time.new(2020, 1, 1, 0, 0))
|
81
|
+
# => [2019-12-01 05:05:00 +0100]
|
82
|
+
|
83
|
+
# You can combine ',' and '-'
|
84
|
+
CronCalc.new('5 5 5-7,10 FEB *').next(5)
|
85
|
+
# => [2024-02-05 05:05:00 +0100, 2024-02-06 05:05:00 +0100, 2024-02-07 05:05:00 +0100, 2024-02-10 05:05:00 +0100, 2025-02-05 05:05:00 +0100]
|
71
86
|
|
72
|
-
|
73
|
-
|
74
|
-
|
87
|
+
# You can use predefined definitions like @daily, @monthly, etc.
|
88
|
+
CronCalc.new('@monthly').next(3, after: Time.new(2024, 1, 1, 0, 0))
|
89
|
+
# => [2024-01-01 00:00:00 +0100, 2024-02-01 00:00:00 +0100, 2024-03-01 00:00:00 +0100]
|
90
|
+
```
|
75
91
|
|
76
92
|
## Contributing
|
77
93
|
|
data/lib/cron_calc/version.rb
CHANGED
data/lib/cron_calc.rb
CHANGED
@@ -7,6 +7,33 @@ require 'time'
|
|
7
7
|
module CronCalc
|
8
8
|
class Error < StandardError; end
|
9
9
|
|
10
|
+
RANGE = {
|
11
|
+
minutes: 0..59,
|
12
|
+
hours: 0..23,
|
13
|
+
days: 1..31,
|
14
|
+
months: 1..12,
|
15
|
+
wdays: 0..6
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
WDAYS = {
|
19
|
+
'SUN' => '0', 'MON' => '1', 'TUE' => '2', 'WED' => '3',
|
20
|
+
'THU' => '4', 'FRI' => '5', 'SAT' => '6'
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
MONTHS = {
|
24
|
+
'JAN' => '1', 'FEB' => '2', 'MAR' => '3', 'APR' => '4',
|
25
|
+
'MAY' => '5', 'JUN' => '6', 'JUL' => '7', 'AUG' => '8',
|
26
|
+
'SEP' => '9', 'OCT' => '10', 'NOV' => '11', 'DEC' => '12'
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
PREDEFINED_DEFINITIONS = {
|
30
|
+
'@yearly' => '0 0 1 1 *', '@annually' => '0 0 1 1 *',
|
31
|
+
'@monthly' => '0 0 1 * *',
|
32
|
+
'@weekly' => '0 0 * * 0',
|
33
|
+
'@daily' => '0 0 * * *', '@midnight' => '0 0 * * *',
|
34
|
+
'@hourly' => '0 * * * *'
|
35
|
+
}.freeze
|
36
|
+
|
10
37
|
def self.new(cron_string)
|
11
38
|
Parser.new(cron_string)
|
12
39
|
end
|
@@ -17,19 +44,12 @@ module CronCalc
|
|
17
44
|
class Parser
|
18
45
|
attr_reader :cron_string, :cron_parts
|
19
46
|
|
20
|
-
RANGE = {
|
21
|
-
minutes: 0..59,
|
22
|
-
hours: 0..23,
|
23
|
-
days: 1..31,
|
24
|
-
months: 1..12,
|
25
|
-
dows: 0..7
|
26
|
-
}.freeze
|
27
|
-
|
28
47
|
def initialize(cron_string)
|
29
48
|
@cron_string = cron_string
|
30
49
|
|
31
50
|
raise 'Cron expression is not supported or invalid' unless cron_string_valid?
|
32
51
|
|
52
|
+
@cron_string = normalize_with(cron_string, PREDEFINED_DEFINITIONS) if @cron_string.start_with? '@'
|
33
53
|
@cron_parts = split_cron_string
|
34
54
|
end
|
35
55
|
|
@@ -42,24 +62,24 @@ module CronCalc
|
|
42
62
|
|
43
63
|
# Calculates the next 'n' occurrences of the cron job from a given start time.
|
44
64
|
# @param count [Integer] The number of occurrences to calculate.
|
45
|
-
# @param
|
65
|
+
# @param after [Time] The start time from which to calculate occurrences.
|
46
66
|
# @param max_years [Integer] The maximum number of years to consider for the period.
|
47
67
|
# @return [Array<Time>] An array of the next 'n' occurrences.
|
48
|
-
def next(count = 1,
|
68
|
+
def next(count = 1, after: Time.now, max_years: 5)
|
49
69
|
occurrences(
|
50
|
-
|
70
|
+
after..(after + (60 * 60 * 24 * 365 * max_years)),
|
51
71
|
count
|
52
72
|
)
|
53
73
|
end
|
54
74
|
|
55
75
|
# Calculates the last 'n' occurrences of the cron job until a given end time.
|
56
76
|
# @param count [Integer] The number of past occurrences to calculate.
|
57
|
-
# @param
|
77
|
+
# @param before [Time] The end time until which to calculate occurrences.
|
58
78
|
# @param max_years [Integer] The maximum number of years to consider for the period.
|
59
79
|
# @return [Array<Time>] An array of the last 'n' occurrences.
|
60
|
-
def last(count = 1,
|
80
|
+
def last(count = 1, before: Time.now, max_years: 5)
|
61
81
|
occurrences(
|
62
|
-
(
|
82
|
+
(before - (60 * 60 * 24 * 365 * max_years))..before,
|
63
83
|
count,
|
64
84
|
reverse: true
|
65
85
|
)
|
@@ -69,7 +89,7 @@ module CronCalc
|
|
69
89
|
|
70
90
|
def occurrences(period, count = nil, reverse: false)
|
71
91
|
time_combinations = generate_time_combinations(period, reverse).lazy
|
72
|
-
wdays = parse_cron_part(:
|
92
|
+
wdays = parse_cron_part(:wdays)
|
73
93
|
|
74
94
|
time_combinations.each_with_object([]) do |(year, month, day, hour, minute), occ|
|
75
95
|
break occ if count && occ.length == count
|
@@ -94,8 +114,8 @@ module CronCalc
|
|
94
114
|
minutes: splitted[0],
|
95
115
|
hours: splitted[1],
|
96
116
|
days: splitted[2],
|
97
|
-
months: splitted[3],
|
98
|
-
|
117
|
+
months: normalize_with(splitted[3], MONTHS),
|
118
|
+
wdays: normalize_with(splitted[4], WDAYS)
|
99
119
|
}
|
100
120
|
end
|
101
121
|
|
@@ -103,31 +123,40 @@ module CronCalc
|
|
103
123
|
%i[minutes hours days months].map { |unit| parse_cron_part(unit) }
|
104
124
|
end
|
105
125
|
|
106
|
-
# rubocop:disable Metrics
|
107
126
|
def parse_cron_part(time_unit)
|
108
127
|
range = RANGE[time_unit]
|
109
128
|
part = cron_parts[time_unit]
|
110
129
|
|
111
|
-
|
130
|
+
if part.include?(',')
|
131
|
+
part.split(',').flat_map { |e| parse_single_element(e, range) }.uniq.sort
|
132
|
+
else
|
133
|
+
parse_single_element(part, range)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def parse_single_element(element, range)
|
138
|
+
case element
|
112
139
|
when '*'
|
113
140
|
range.to_a
|
114
|
-
when /,/
|
115
|
-
part.split(',').map(&:to_i)
|
116
141
|
when /-/
|
117
|
-
(
|
142
|
+
(element.split('-').first.to_i..element.split('-').last.to_i).to_a
|
118
143
|
when %r{/}
|
119
|
-
range.step(
|
144
|
+
range.step(element.split('/').last.to_i).to_a
|
120
145
|
else
|
121
|
-
[
|
146
|
+
[element.to_i]
|
122
147
|
end
|
123
148
|
end
|
124
|
-
|
149
|
+
|
150
|
+
def normalize_with(string, mapping)
|
151
|
+
mapping.inject(string) { |str, (k, v)| str.gsub(k, v) }
|
152
|
+
end
|
125
153
|
|
126
154
|
def cron_string_valid?
|
155
|
+
predefined = /\A@(yearly|annually|monthly|weekly|daily|midnight|hourly)\z/
|
127
156
|
# rubocop:disable Layout/LineLength
|
128
|
-
|
157
|
+
standard = %r{\A(\*|(\*/[0-5]?\d)|([0-5]?\d)(-(?:[0-5]?\d))?(,([0-5]?\d)(-(?:[0-5]?\d))?)*) (\*|(\*/[01]?\d|2[0-3])|([01]?\d|2[0-3])(-(?:[01]?\d|2[0-3]))?(,([01]?\d|2[0-3])(-(?:[01]?\d|2[0-3]))?)*|(\*/\d+)) (\*|(\*/[12]?\d|3[01])|([12]?\d|3[01])(-(?:[12]?\d|3[01]))?(,([12]?\d|3[01])(-(?:[12]?\d|3[01]))?)*|(\*/\d+)) (\*|(\*/[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|[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|[1-9]|1[0-2]))?)*|(\*/\d+)) (\*|(\*/[0-6])|(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])(-(?:SUN|MON|TUE|WED|THU|FRI|SAT|[0-6]))?)*|(\*/[0-6]+))\z}
|
129
158
|
# rubocop:enable Layout/LineLength
|
130
|
-
cron_string.match?(
|
159
|
+
cron_string.match?(predefined) || cron_string.match?(standard)
|
131
160
|
end
|
132
161
|
end
|
133
162
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cron_calc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.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-
|
11
|
+
date: 2023-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: |-
|
14
14
|
Calculates cron job occurrences within a specified period
|