Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
/spec/reports/
/tmp/

# rubymine
.idea

# rspec failure tracking
.rspec_status
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Indonesian translations. ([#505](https://github.com/seejohnrun/ice_cube/pull/505)) by [@achmiral](https://github.com/achmiral)
- Support for BYSETPOS

### Changed
- Removed use of `delegate` method added in [66f1d797](https://github.com/ice-cube-ruby/ice_cube/commit/66f1d797092734563bfabd2132c024c7d087f683) , reverting to previous implementation. ([#522](https://github.com/ice-cube-ruby/ice_cube/pull/522)) by [@pacso](https://github.com/pacso)
Expand Down
1 change: 0 additions & 1 deletion ice_cube.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Gem::Specification.new do |s|
s.version = IceCube::VERSION
s.platform = Gem::Platform::RUBY
s.files = Dir["lib/**/*.rb", "config/**/*.yml"]
s.test_files = Dir.glob("spec/*.rb")
s.require_paths = ["lib"]

s.add_development_dependency("rake")
Expand Down
4 changes: 4 additions & 0 deletions lib/ice_cube.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ module Validations
autoload :YearlyInterval, "ice_cube/validations/yearly_interval"
autoload :HourlyInterval, "ice_cube/validations/hourly_interval"

autoload :WeeklyBySetPos, "ice_cube/validations/weekly_by_set_pos"
autoload :MonthlyBySetPos, "ice_cube/validations/monthly_by_set_pos"
autoload :YearlyBySetPos, "ice_cube/validations/yearly_by_set_pos"

autoload :HourOfDay, "ice_cube/validations/hour_of_day"
autoload :MonthOfYear, "ice_cube/validations/month_of_year"
autoload :MinuteOfHour, "ice_cube/validations/minute_of_hour"
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/builders/string_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def ordinal(number)
ord = IceCube::I18n.t("ice_cube.integer.ordinals")[number] ||
IceCube::I18n.t("ice_cube.integer.ordinals")[number % 10] ||
IceCube::I18n.t("ice_cube.integer.ordinals")[:default]
number >= 0 ? ord : IceCube::I18n.t("ice_cube.integer.negative", ordinal: ord)
(number >= 0) ? ord : IceCube::I18n.t("ice_cube.integer.negative", ordinal: ord)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/ice_cube/input_alignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ def interval_validation
end

def interval_value
@interval_value ||= rule_part == :interval ? value : interval_validation.interval
@interval_value ||= (rule_part == :interval) ? value : interval_validation.interval
end

def fixed_validations
@fixed_validations ||= @rule.validations.values.flatten.select { |v|
interval_type = (v.type == :wday ? :day : v.type)
interval_type = ((v.type == :wday) ? :day : v.type)
v.class < Validations::FixedValue &&
interval_type == rule.base_interval_validation.type
}
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/null_i18n.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module NullI18n
def self.t(key, options = {})
base = key.to_s.split(".").reduce(config) { |hash, current_key| hash[current_key] }

base = base[options[:count] == 1 ? "one" : "other"] if options[:count]
base = base[(options[:count] == 1) ? "one" : "other"] if options[:count]

case base
when Hash
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/occurrence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def to_s(format = nil)
else
t0, t1 = start_time.to_s, end_time.to_s
end
duration > 0 ? "#{t0} - #{t1}" : t0
(duration > 0) ? "#{t0} - #{t1}" : t0
end

def overnight?
Expand Down
13 changes: 12 additions & 1 deletion lib/ice_cube/parsers/ical_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ module IceCube
class IcalParser
def self.schedule_from_ical(ical_string, options = {})
data = {}

# First join lines that are wrapped
lines = []
ical_string.each_line do |line|
if lines[-1] && line =~ /\A[ \t].+/
lines[-1] = lines[-1].strip + line.sub(/\A[ \t]+/, "")
else
lines << line
end
end

lines.each do |line|
(property, value) = line.split(":")
(property, _tzid) = property.split(";")
case property
Expand Down Expand Up @@ -75,7 +86,7 @@ def self.rule_from_ical(ical)
when "BYYEARDAY"
validations[:day_of_year] = value.split(",").map(&:to_i)
when "BYSETPOS"
# noop
params[:validations][:by_set_pos] = value.split(",").collect(&:to_i)
else
validations[name] = nil # invalid type
end
Expand Down
1 change: 1 addition & 0 deletions lib/ice_cube/rules/monthly_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class MonthlyRule < ValidatedRule
# include Validations::DayOfYear # n/a

include Validations::MonthlyInterval
include Validations::MonthlyBySetPos

def initialize(interval = 1)
super
Expand Down
1 change: 1 addition & 0 deletions lib/ice_cube/rules/weekly_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class WeeklyRule < ValidatedRule
# include Validations::DayOfYear # n/a

include Validations::WeeklyInterval
include Validations::WeeklyBySetPos

attr_reader :week_start

Expand Down
1 change: 1 addition & 0 deletions lib/ice_cube/rules/yearly_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class YearlyRule < ValidatedRule
include Validations::DayOfYear

include Validations::YearlyInterval
include Validations::YearlyBySetPos

def initialize(interval = 1)
super
Expand Down
2 changes: 1 addition & 1 deletion lib/ice_cube/schedule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def previous_occurrences(num, from)
from = TimeUtil.match_zone(from, start_time) or raise ArgumentError, "Time required, got #{from.inspect}"
return [] if from <= start_time
a = enumerate_occurrences(start_time, from - 1).to_a
a.size > num ? a[-1 * num, a.size] : a
(a.size > num) ? a[-1 * num, a.size] : a
end

# The remaining occurrences (same requirements as all_occurrences)
Expand Down
54 changes: 43 additions & 11 deletions lib/ice_cube/time_util.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require "date"
require "time"
require "active_support"
require "active_support/core_ext"

module IceCube
module TimeUtil
Expand Down Expand Up @@ -51,7 +53,7 @@ def self.match_zone(input_time, reference)
else
time.getlocal(reference.utc_offset)
end
Date === input_time ? beginning_of_date(time, reference) : time
(Date === input_time) ? beginning_of_date(time, reference) : time
end

# Ensure that this is either nil, or a time
Expand Down Expand Up @@ -193,6 +195,36 @@ def self.which_occurrence_in_month(time, wday)
[nth_occurrence_of_weekday, this_weekday_in_month_count]
end

# Use Activesupport CoreExt functions to manipulate time
def self.start_of_month time
time.beginning_of_month
end

# Use Activesupport CoreExt functions to manipulate time
def self.end_of_month time
time.end_of_month
end

# Use Activesupport CoreExt functions to manipulate time
def self.start_of_year time
time.beginning_of_year
end

# Use Activesupport CoreExt functions to manipulate time
def self.end_of_year time
time.end_of_year
end

# Use Activesupport CoreExt functions to manipulate time
def self.previous_month time
time - 1.month
end

# Use Activesupport CoreExt functions to manipulate time
def self.previous_year time
time - 1.year
end

# Get the days in the month for +time
def self.days_in_month(time)
date = Date.new(time.year, time.month, 1)
Expand Down Expand Up @@ -286,12 +318,12 @@ def to_time
def add(type, val)
type = :day if type == :wday
@time += case type
when :year then TimeUtil.days_in_n_years(@time, val) * ONE_DAY
when :month then TimeUtil.days_in_n_months(@time, val) * ONE_DAY
when :day then val * ONE_DAY
when :hour then val * ONE_HOUR
when :min then val * ONE_MINUTE
when :sec then val
when :year then TimeUtil.days_in_n_years(@time, val) * ONE_DAY
when :month then TimeUtil.days_in_n_months(@time, val) * ONE_DAY
when :day then val * ONE_DAY
when :hour then val * ONE_HOUR
when :min then val * ONE_MINUTE
when :sec then val
end
end

Expand All @@ -318,20 +350,20 @@ def sec=(value)
end

def clear_sec
@time.sec > 0 ? @time -= @time.sec : @time
(@time.sec > 0) ? @time -= @time.sec : @time
end

def clear_min
@time.min > 0 ? @time -= (@time.min * ONE_MINUTE) : @time
(@time.min > 0) ? @time -= (@time.min * ONE_MINUTE) : @time
end

def clear_hour
@time.hour > 0 ? @time -= (@time.hour * ONE_HOUR) : @time
(@time.hour > 0) ? @time -= (@time.hour * ONE_HOUR) : @time
end

# Move to the first of the month, 0 hours
def clear_day
@time.day > 1 ? @time -= (@time.day - 1) * ONE_DAY : @time
(@time.day > 1) ? @time -= (@time.day - 1) * ONE_DAY : @time
end

# Clear to january 1st
Expand Down
3 changes: 2 additions & 1 deletion lib/ice_cube/validated_rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class ValidatedRule < Rule
:base_sec, :base_min, :base_day, :base_hour, :base_month, :base_wday,
:day_of_year, :second_of_minute, :minute_of_hour, :day_of_month,
:hour_of_day, :month_of_year, :day_of_week,
:interval
:interval,
:by_set_pos
]

attr_reader :validations
Expand Down
4 changes: 2 additions & 2 deletions lib/ice_cube/validations/day_of_week.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ def dst_adjust?

def validate(step_time, start_time)
wday = step_time.wday
offset = day < wday ? (7 - wday + day) : (day - wday)
offset = (day < wday) ? (7 - wday + day) : (day - wday)
wrapper = TimeUtil::TimeWrapper.new(step_time)
wrapper.add :day, offset
loop do
which_occ, num_occ = TimeUtil.which_occurrence_in_month(wrapper.to_time, day)
this_occ = occ < 0 ? (num_occ + occ + 1) : occ
this_occ = (occ < 0) ? (num_occ + occ + 1) : occ
break offset if which_occ == this_occ
wrapper.add :day, 7
offset += 7
Expand Down
4 changes: 2 additions & 2 deletions lib/ice_cube/validations/day_of_year.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ def dst_adjust?

def validate(step_time, start_time)
days_in_year = TimeUtil.days_in_year(step_time)
yday = day < 0 ? day + days_in_year + 1 : day
yday = (day < 0) ? day + days_in_year + 1 : day
offset = yday - step_time.yday
offset >= 0 ? offset : offset + days_in_year
(offset >= 0) ? offset : offset + days_in_year
end

def build_s(builder)
Expand Down
6 changes: 3 additions & 3 deletions lib/ice_cube/validations/fixed_value.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def validate(time, start_time)
def validate_interval_lock(time, start_time)
t0 = starting_unit(start_time)
t1 = time.send(type)
t0 >= t1 ? t0 - t1 : INTERVALS[type] - t1 + t0
(t0 >= t1) ? t0 - t1 : INTERVALS[type] - t1 + t0
end

# Lock the hour if explicitly set by hour_of_day, but allow for the nearest
Expand Down Expand Up @@ -73,11 +73,11 @@ def validate_day_lock(time, start_time)
if value && value > 0
until_next_month = days_in_month + sleeps
else
until_next_month = start < 28 ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month = (start < 28) ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month += sleeps - month_overflow
end

sleeps >= 0 ? sleeps : until_next_month
(sleeps >= 0) ? sleeps : until_next_month
end

def starting_unit(start_time)
Expand Down
6 changes: 3 additions & 3 deletions lib/ice_cube/validations/lock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def validate(time, start_time)
def validate_interval_lock(time, start_time)
t0 = starting_unit(start_time)
t1 = time.send(type)
t0 >= t1 ? t0 - t1 : INTERVALS[type] - t1 + t0
(t0 >= t1) ? t0 - t1 : INTERVALS[type] - t1 + t0
end

# Lock the hour if explicitly set by hour_of_day, but allow for the nearest
Expand Down Expand Up @@ -73,11 +73,11 @@ def validate_day_lock(time, start_time)
if value && value > 0
until_next_month = days_in_month + sleeps
else
until_next_month = start < 28 ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month = (start < 28) ? days_in_month : TimeUtil.days_to_next_month(date)
until_next_month += sleeps - month_overflow
end

sleeps >= 0 ? sleeps : until_next_month
(sleeps >= 0) ? sleeps : until_next_month
end

def starting_unit(start_time)
Expand Down
73 changes: 73 additions & 0 deletions lib/ice_cube/validations/monthly_by_set_pos.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module IceCube
module Validations::MonthlyBySetPos
def by_set_pos(*by_set_pos)
by_set_pos.flatten!
by_set_pos.each do |set_pos|
unless (-366..366).cover?(set_pos) && set_pos != 0
raise ArgumentError, "Expecting number in [-366, -1] or [1, 366], got #{set_pos} (#{by_set_pos})"
end
end

@by_set_pos = by_set_pos
replace_validations_for(:by_set_pos, [Validation.new(by_set_pos, self)])
self
end

class Validation
attr_reader :rule, :by_set_pos

def initialize(by_set_pos, rule)
@by_set_pos = by_set_pos
@rule = rule
end

def type
:day
end

def dst_adjust?
true
end

def validate(step_time, start_time)
start_of_month = TimeUtil.build_in_zone([step_time.year, step_time.month, 1, 0, 0, 0], step_time)
eom_date = Date.new(step_time.year, step_time.month, -1)
end_of_month = TimeUtil.build_in_zone([eom_date.year, eom_date.month, eom_date.day, 23, 59, 59], step_time)

# Needs to start on the first day of the month
new_schedule = IceCube::Schedule.new(IceCube::TimeUtil.build_in_zone([start_of_month.year, start_of_month.month, start_of_month.day, step_time.hour, step_time.min, step_time.sec], start_of_month)) do |s|
s.add_recurrence_rule(IceCube::Rule.from_hash(rule.to_hash.except(:by_set_pos, :count, :until)))
end

occurrences = new_schedule.occurrences_between(start_of_month, end_of_month)
index = occurrences.index(step_time)
if index.nil?
1
else
positive_set_pos = index + 1
negative_set_pos = index - occurrences.length

if @by_set_pos.include?(positive_set_pos) || @by_set_pos.include?(negative_set_pos)
0
else
1
end
end
end

def build_s(builder)
builder.piece(:by_set_pos) << by_set_pos
end

def build_hash(builder)
builder[:by_set_pos] = by_set_pos
end

def build_ical(builder)
builder["BYSETPOS"] << by_set_pos
end

nil
end
end
end
Loading