Class: Rufus::Scheduler::CronLine
- Inherits:
-
Object
- Object
- Rufus::Scheduler::CronLine
- Defined in:
- lib/rufus/scheduler/cronline.rb
Overview
A ‘cron line’ is a line in the sense of a crontab (man 5 crontab) file line.
Instance Attribute Summary collapse
-
#days ⇒ Object
readonly
Returns the value of attribute days.
-
#hours ⇒ Object
readonly
Returns the value of attribute hours.
-
#minutes ⇒ Object
readonly
Returns the value of attribute minutes.
-
#months ⇒ Object
readonly
Returns the value of attribute months.
-
#original ⇒ Object
readonly
The string used for creating this cronline instance.
-
#seconds ⇒ Object
readonly
Returns the value of attribute seconds.
-
#timezone ⇒ Object
readonly
Returns the value of attribute timezone.
-
#weekdays ⇒ Object
readonly
Returns the value of attribute weekdays.
Instance Method Summary collapse
-
#brute_frequency ⇒ Object
Returns the shortest delta between two potential occurences of the schedule described by this cronline.
-
#frequency ⇒ Object
Returns a quickly computed approximation of the frequency for this cron line.
-
#initialize(line) ⇒ CronLine
constructor
A new instance of CronLine.
-
#matches?(time) ⇒ Boolean
Returns true if the given time matches this cron line.
- #next_second(time) ⇒ Object
-
#next_time(from = Time.now) ⇒ Object
Returns the next time that this cron line is supposed to ‘fire’.
- #prev_second(time) ⇒ Object
-
#previous_time(from = Time.now) ⇒ Object
Returns the previous time the cronline matched.
-
#to_array ⇒ Object
Returns an array of 6 arrays (seconds, minutes, hours, days, months, weekdays).
Constructor Details
#initialize(line) ⇒ CronLine
Returns a new instance of CronLine.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rufus/scheduler/cronline.rb', line 49 def initialize(line) raise ArgumentError.new( "not a string: #{line.inspect}" ) unless line.is_a?(String) @original = line items = line.split @timezone = items.pop if ZoTime.is_timezone?(items.last) raise ArgumentError.new( "not a valid cronline : '#{line}'" ) unless items.length == 5 or items.length == 6 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) @months = parse_item(items[3 + offset], 1, 12) @weekdays, @monthdays = parse_weekdays(items[4 + offset]) [ @seconds, @minutes, @hours, @months ].each do |es| raise ArgumentError.new( "invalid cronline: '#{line}'" ) if es && es.find { |e| ! e.is_a?(Fixnum) } end end |
Instance Attribute Details
#days ⇒ Object (readonly)
Returns the value of attribute days.
43 44 45 |
# File 'lib/rufus/scheduler/cronline.rb', line 43 def days @days end |
#hours ⇒ Object (readonly)
Returns the value of attribute hours.
42 43 44 |
# File 'lib/rufus/scheduler/cronline.rb', line 42 def hours @hours end |
#minutes ⇒ Object (readonly)
Returns the value of attribute minutes.
41 42 43 |
# File 'lib/rufus/scheduler/cronline.rb', line 41 def minutes @minutes end |
#months ⇒ Object (readonly)
Returns the value of attribute months.
44 45 46 |
# File 'lib/rufus/scheduler/cronline.rb', line 44 def months @months end |
#original ⇒ Object (readonly)
The string used for creating this cronline instance.
38 39 40 |
# File 'lib/rufus/scheduler/cronline.rb', line 38 def original @original end |
#seconds ⇒ Object (readonly)
Returns the value of attribute seconds.
40 41 42 |
# File 'lib/rufus/scheduler/cronline.rb', line 40 def seconds @seconds end |
#timezone ⇒ Object (readonly)
Returns the value of attribute timezone.
47 48 49 |
# File 'lib/rufus/scheduler/cronline.rb', line 47 def timezone @timezone end |
#weekdays ⇒ Object (readonly)
Returns the value of attribute weekdays.
45 46 47 |
# File 'lib/rufus/scheduler/cronline.rb', line 45 def weekdays @weekdays end |
Instance Method Details
#brute_frequency ⇒ Object
Returns the shortest delta between two potential occurences of the schedule described by this cronline.
.
For a simple cronline like “*/5 * * * *”, obviously the frequency is five minutes. Why does this method look at a whole year of #next_time ?
Consider “* * * * sun#2,sun#3”, the computed frequency is 1 week (the shortest delta is the one between the second sunday and the third sunday). This method takes no chance and runs next_time for the span of a whole year and keeps the shortest.
Of course, this method can get VERY slow if you call on it a second- based cronline…
Since it’s a rarely used method, I haven’t taken the time to make it smarter/faster.
One obvious improvement would be to cache the result once computed…
See github.com/jmettraux/rufus-scheduler/issues/89 for a discussion about this method.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/rufus/scheduler/cronline.rb', line 250 def brute_frequency delta = 366 * DAY_S t0 = previous_time(Time.local(2000, 1, 1)) loop do break if delta <= 1 break if delta <= 60 && @seconds && @seconds.size == 1 t1 = next_time(t0) d = t1 - t0 delta = d if d < delta break if @months == nil && t1.month == 2 break if t1.year >= 2001 t0 = t1 end delta end |
#frequency ⇒ Object
Returns a quickly computed approximation of the frequency for this cron line.
#brute_frequency, on the other hand, will compute the frequency by examining a whole year, that can take more than seconds for a seconds level cron…
214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/rufus/scheduler/cronline.rb', line 214 def frequency return brute_frequency unless @seconds && @seconds.length > 1 secs = toa(@seconds) secs[1..-1].inject([ secs[0], 60 ]) { |(prev, delta), sec| d = sec - prev [ sec, d < delta ? d : delta ] }[1] end |
#matches?(time) ⇒ Boolean
Returns true if the given time matches this cron line.
84 85 86 87 88 89 90 91 92 93 |
# File 'lib/rufus/scheduler/cronline.rb', line 84 def matches?(time) time = ZoTime.new(time.to_f, @timezone || ENV['TZ']).time return false unless sub_match?(time, :sec, @seconds) return false unless sub_match?(time, :min, @minutes) return false unless sub_match?(time, :hour, @hours) return false unless date_match?(time) true end |
#next_second(time) ⇒ Object
274 275 276 277 278 279 280 281 282 283 |
# File 'lib/rufus/scheduler/cronline.rb', line 274 def next_second(time) secs = toa(@seconds) return secs.first + 60 - time.sec if time.sec > secs.last secs.shift while secs.first < time.sec secs.first - time.sec end |
#next_time(from = Time.now) ⇒ Object
Returns the next time that this cron line is supposed to ‘fire’
This is raw, 3 secs to iterate over 1 year on my macbook :( brutal. (Well, I was wrong, takes 0.001 sec on 1.8.7 and 1.9.1)
This method accepts an optional Time parameter. It’s the starting point for the ‘search’. By default, it’s Time.now
Note that the time instance returned will be in the same time zone that the given start point Time (thus a result in the local time zone will be passed if no start time is specified (search start time set to Time.now))
Rufus::Scheduler::CronLine.new('30 7 * * *').next_time(
Time.mktime(2008, 10, 24, 7, 29))
#=> Fri Oct 24 07:30:00 -0500 2008
Rufus::Scheduler::CronLine.new('30 7 * * *').next_time(
Time.utc(2008, 10, 24, 7, 29))
#=> Fri Oct 24 07:30:00 UTC 2008
Rufus::Scheduler::CronLine.new('30 7 * * *').next_time(
Time.utc(2008, 10, 24, 7, 29)).localtime
#=> Fri Oct 24 02:30:00 -0500 2008
(Thanks to K Liu for the note and the examples)
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/rufus/scheduler/cronline.rb', line 122 def next_time(from=Time.now) time = nil zotime = ZoTime.new(from.to_i + 1, @timezone || ENV['TZ']) loop do time = zotime.time unless date_match?(time) zotime.add((24 - time.hour) * 3600 - time.min * 60 - time.sec) next end unless sub_match?(time, :hour, @hours) zotime.add((60 - time.min) * 60 - time.sec) next end unless sub_match?(time, :min, @minutes) zotime.add(60 - time.sec) next end unless sub_match?(time, :sec, @seconds) zotime.add(next_second(time)) next end break end time end |
#prev_second(time) ⇒ Object
285 286 287 288 289 290 291 292 293 294 |
# File 'lib/rufus/scheduler/cronline.rb', line 285 def prev_second(time) secs = toa(@seconds) return time.sec + 60 - secs.last if time.sec < secs.first secs.pop while time.sec < secs.last time.sec - secs.last end |
#previous_time(from = Time.now) ⇒ Object
Returns the previous time the cronline matched. It’s like next_time, but for the past.
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/rufus/scheduler/cronline.rb', line 157 def previous_time(from=Time.now) time = nil zotime = ZoTime.new(from.to_i - 1, @timezone || ENV['TZ']) loop do time = zotime.time unless date_match?(time) zotime.substract(time.hour * 3600 + time.min * 60 + time.sec + 1) next end unless sub_match?(time, :hour, @hours) zotime.substract(time.min * 60 + time.sec + 1) next end unless sub_match?(time, :min, @minutes) zotime.substract(time.sec + 1) next end unless sub_match?(time, :sec, @seconds) zotime.substract(prev_second(time)) next end break end time end |
#to_array ⇒ Object
Returns an array of 6 arrays (seconds, minutes, hours, days, months, weekdays). This method is used by the cronline unit tests.
193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/rufus/scheduler/cronline.rb', line 193 def to_array [ toa(@seconds), toa(@minutes), toa(@hours), toa(@days), toa(@months), toa(@weekdays), toa(@monthdays), @timezone ] end |