lib/rufus/scheduler/cronline.rb in rufus-scheduler-3.0.9 vs lib/rufus/scheduler/cronline.rb in rufus-scheduler-3.1.0
- old
+ new
@@ -1,7 +1,7 @@
#--
-# Copyright (c) 2006-2014, John Mettraux, jmettraux@gmail.com
+# Copyright (c) 2006-2015, John Mettraux, jmettraux@gmail.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
@@ -20,11 +20,13 @@
# THE SOFTWARE.
#
# Made in Japan.
#++
+require 'set'
+
class Rufus::Scheduler
#
# A 'cron line' is a line in the sense of a crontab
# (man 5 crontab) file line.
@@ -52,12 +54,11 @@
@original = line
items = line.split
- @timezone = (TZInfo::Timezone.get(items.last) rescue nil)
- items.pop if @timezone
+ @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
@@ -80,14 +81,12 @@
# Returns true if the given time matches this cron line.
#
def matches?(time)
- time = Time.at(time) unless time.kind_of?(Time)
+ time = ZoTime.new(time.to_f, @timezone || ENV['TZ']).time
- time = @timezone.utc_to_local(time.getutc) if @timezone
-
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
@@ -120,111 +119,122 @@
#
# (Thanks to K Liu for the note and the examples)
#
def next_time(from=Time.now)
- time = local_time(from)
- time = round_to_seconds(time)
+ time = nil
+ zotime = ZoTime.new(from.to_i + 1, @timezone || ENV['TZ'])
- # start at the next second
- time = time + 1
-
loop do
+
+ time = zotime.time
+
unless date_match?(time)
- dst = time.isdst
- time += (24 - time.hour) * 3600 - time.min * 60 - time.sec
- time -= 3600 if time.isdst != dst # not necessary for winter, but...
+ zotime.add((24 - time.hour) * 3600 - time.min * 60 - time.sec)
next
end
unless sub_match?(time, :hour, @hours)
- time += (60 - time.min) * 60 - time.sec; next
+ zotime.add((60 - time.min) * 60 - time.sec)
+ next
end
unless sub_match?(time, :min, @minutes)
- time += 60 - time.sec; next
+ zotime.add(60 - time.sec)
+ next
end
unless sub_match?(time, :sec, @seconds)
- time += 1; next
+ zotime.add(next_second(time))
+ next
end
break
end
- global_time(time, from.utc?)
-
- rescue TZInfo::PeriodNotFound
-
- next_time(from + 3600)
+ time
end
# Returns the previous time the cronline matched. It's like next_time, but
# for the past.
#
def previous_time(from=Time.now)
- time = local_time(from)
- time = round_to_seconds(time)
+ time = nil
+ zotime = ZoTime.new(from.to_i - 1, @timezone || ENV['TZ'])
- # start at the previous second
- time = time - 1
-
loop do
+
+ time = zotime.time
+
unless date_match?(time)
- time -= time.hour * 3600 + time.min * 60 + time.sec + 1; next
+ zotime.substract(time.hour * 3600 + time.min * 60 + time.sec + 1)
+ next
end
unless sub_match?(time, :hour, @hours)
- time -= time.min * 60 + time.sec + 1; next
+ zotime.substract(time.min * 60 + time.sec + 1)
+ next
end
unless sub_match?(time, :min, @minutes)
- time -= time.sec + 1; next
+ zotime.substract(time.sec + 1)
+ next
end
unless sub_match?(time, :sec, @seconds)
- time -= 1; next
+ zotime.substract(prev_second(time))
+ next
end
break
end
- global_time(time, from.utc?)
+ time
+ end
- rescue TZInfo::PeriodNotFound
-
- previous_time(time)
+ if RUBY_VERSION >= '1.9'
+ def toa(item)
+ item == nil ? nil : item.to_a
+ end
+ else
+ def toi(item); item.is_a?(String) ? item.hash.abs : item.to_i; end
+ protected :toi
+ def toa(item)
+ item.is_a?(Set) ? item.to_a.sort_by { |e| toi(e) } : item
+ end
end
+ protected :toa
# Returns an array of 6 arrays (seconds, minutes, hours, days,
# months, weekdays).
# This method is used by the cronline unit tests.
#
def to_array
[
- @seconds,
- @minutes,
- @hours,
- @days,
- @months,
- @weekdays,
- @monthdays,
- @timezone ? @timezone.name : nil
+ toa(@seconds),
+ toa(@minutes),
+ toa(@hours),
+ toa(@days),
+ toa(@months),
+ toa(@weekdays),
+ toa(@monthdays),
+ @timezone
]
end
# 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, that can take more than seconds for a seconds
+ # examining a whole year, that can take more than seconds for a seconds
# level cron...
#
def frequency
return brute_frequency unless @seconds && @seconds.length > 1
delta = 60
- prev = @seconds[0]
+ secs = toa(@seconds)
+ prev = secs[0]
- @seconds[1..-1].each do |sec|
+ secs[1..-1].each do |sec|
d = sec - prev
delta = d if d < delta
end
delta
@@ -268,20 +278,40 @@
t1 = next_time(t0)
d = t1 - t0
delta = d if d < delta
break if @months == nil && t1.month == 2
- break if t1.year == 2001
+ break if t1.year >= 2001
t0 = t1
end
delta
end
protected
+ def next_second(time)
+
+ secs = @seconds.sort
+
+ return secs.last + 60 - time.sec if time.sec > secs.last
+
+ secs.shift while secs.first < time.sec
+
+ secs.first - time.sec
+ end
+
+ def prev_second(time)
+
+ secs = @seconds.sort
+
+ secs.pop while time.sec < secs.last
+
+ time.sec - secs.last
+ end
+
WEEKDAYS = %w[ sun mon tue wed thu fri sat ]
DAY_S = 24 * 3600
WEEK_S = 7 * DAY_S
def parse_weekdays(item)
@@ -334,11 +364,11 @@
raise ArgumentError.new(
"found duplicates in #{item.inspect}"
) if r.uniq.size < r.size
- r
+ Set.new(r)
end
RANGE_REGEX = /^(\*|\d{1,2})(?:-(\d{1,2}))?(?:\/(\d{1,2}))?$/
def parse_range(item, min, max)
@@ -432,35 +462,9 @@
break if d.month != date.month
neg = neg - 1
end
[ "#{WEEKDAYS[date.wday]}##{pos}", "#{WEEKDAYS[date.wday]}##{neg}" ]
- end
-
- def local_time(time)
-
- @timezone ? @timezone.utc_to_local(time.getutc) : time
- end
-
- def global_time(time, from_in_utc)
-
- if @timezone
- time =
- begin
- @timezone.local_to_utc(time)
- rescue TZInfo::AmbiguousTime
- @timezone.local_to_utc(time, time.isdst)
- end
- time = time.getlocal unless from_in_utc
- end
-
- time
- end
-
- def round_to_seconds(time)
-
- # Ruby 1.8 doesn't have #round
- time.respond_to?(:round) ? time.round : time - time.usec * 1e-6
end
end
end