ice_cube 0.16.0 → 0.16.4

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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/config/locales/de.yml +1 -1
  3. data/config/locales/en.yml +7 -46
  4. data/config/locales/es.yml +47 -83
  5. data/config/locales/fr.yml +2 -2
  6. data/config/locales/it.yml +179 -0
  7. data/config/locales/ja.yml +52 -29
  8. data/config/locales/nl.yml +133 -0
  9. data/config/locales/pt-BR.yml +178 -0
  10. data/config/locales/sv.yml +1 -1
  11. data/lib/ice_cube/i18n.rb +11 -12
  12. data/lib/ice_cube/input_alignment.rb +89 -0
  13. data/lib/ice_cube/null_i18n.rb +12 -6
  14. data/lib/ice_cube/occurrence.rb +25 -23
  15. data/lib/ice_cube/parsers/ical_parser.rb +8 -5
  16. data/lib/ice_cube/rule.rb +5 -13
  17. data/lib/ice_cube/rules/daily_rule.rb +9 -0
  18. data/lib/ice_cube/rules/hourly_rule.rb +9 -0
  19. data/lib/ice_cube/rules/minutely_rule.rb +9 -0
  20. data/lib/ice_cube/rules/monthly_rule.rb +9 -0
  21. data/lib/ice_cube/rules/secondly_rule.rb +9 -0
  22. data/lib/ice_cube/rules/weekly_rule.rb +30 -9
  23. data/lib/ice_cube/rules/yearly_rule.rb +9 -0
  24. data/lib/ice_cube/schedule.rb +30 -29
  25. data/lib/ice_cube/single_occurrence_rule.rb +4 -0
  26. data/lib/ice_cube/time_util.rb +65 -46
  27. data/lib/ice_cube/validated_rule.rb +18 -24
  28. data/lib/ice_cube/validations/count.rb +1 -2
  29. data/lib/ice_cube/validations/daily_interval.rb +5 -1
  30. data/lib/ice_cube/validations/day.rb +6 -2
  31. data/lib/ice_cube/validations/day_of_month.rb +5 -0
  32. data/lib/ice_cube/validations/day_of_week.rb +1 -1
  33. data/lib/ice_cube/validations/hour_of_day.rb +23 -0
  34. data/lib/ice_cube/validations/hourly_interval.rb +2 -0
  35. data/lib/ice_cube/validations/minute_of_hour.rb +16 -0
  36. data/lib/ice_cube/validations/minutely_interval.rb +2 -0
  37. data/lib/ice_cube/validations/month_of_year.rb +6 -1
  38. data/lib/ice_cube/validations/monthly_interval.rb +4 -1
  39. data/lib/ice_cube/validations/schedule_lock.rb +4 -0
  40. data/lib/ice_cube/validations/second_of_minute.rb +19 -3
  41. data/lib/ice_cube/validations/secondly_interval.rb +2 -0
  42. data/lib/ice_cube/validations/until.rb +1 -2
  43. data/lib/ice_cube/validations/weekly_interval.rb +0 -2
  44. data/lib/ice_cube/version.rb +1 -1
  45. data/lib/ice_cube.rb +1 -3
  46. data/spec/spec_helper.rb +32 -9
  47. metadata +10 -7
@@ -0,0 +1,178 @@
1
+ pt-BR:
2
+ ice_cube:
3
+ pieces_connector: ' / '
4
+ not: 'exceto %{target}'
5
+ not_on: 'exceto o dia %{target}'
6
+ date:
7
+ formats:
8
+ default: '%-d %B %Y'
9
+ month_names:
10
+ -
11
+ - Janeiro
12
+ - Fevereiro
13
+ - Março
14
+ - Abril
15
+ - Maio
16
+ - Junho
17
+ - Julho
18
+ - Agosto
19
+ - Setembro
20
+ - Outubro
21
+ - Novembro
22
+ - Dezembro
23
+ day_names:
24
+ - Domingo
25
+ - Segunda
26
+ - Terça
27
+ - Quarta
28
+ - Quinta
29
+ - Sexta
30
+ - Sábado
31
+ times:
32
+ other: '%{count} vezes'
33
+ one: '%{count} vez'
34
+ until: 'até %{date}'
35
+ days_of_week: '%{segments} %{day}'
36
+ days_of_month:
37
+ other: '%{segments} dias do mês'
38
+ one: '%{segments} dia'
39
+ days_of_year:
40
+ other: '%{segments} dias do ano'
41
+ one: '%{segments} dia'
42
+ at_hours_of_the_day:
43
+ other: às %{segments} horas
44
+ one: à %{segments} hora
45
+ on_minutes_of_hour:
46
+ other: aos %{segments} minutos
47
+ one: ao %{segments} minuto
48
+ at_seconds_of_minute:
49
+ other: aos %{segments} segundos
50
+ one: ao %{segments} segundo
51
+ on_seconds_of_minute:
52
+ other: aos %{segments} segundos
53
+ one: ao %{segments} segundo
54
+ each_second:
55
+ one: A cada segundo
56
+ other: A cada %{count} segundos
57
+ each_minute:
58
+ one: A cada minuto
59
+ other: A cada %{count} minutos
60
+ each_hour:
61
+ one: A cada hora
62
+ other: A cada %{count} horas
63
+ each_day:
64
+ one: Diariamente
65
+ other: A cada %{count} dias
66
+ each_week:
67
+ one: Semanalmente
68
+ other: A cada %{count} semanas
69
+ each_month:
70
+ one: Mensalmente
71
+ other: A cada %{count} meses
72
+ each_year:
73
+ one: Anualmente
74
+ other: A cada %{count} anos
75
+ 'on': no %{sentence}
76
+ in: 'em %{target}'
77
+ integer:
78
+ negative: '%{ordinal} depois que acabar'
79
+ literal_ordinals:
80
+ -1: último
81
+ -2: penúltimo
82
+ ordinal: '%{number}%{ordinal}'
83
+ ordinals:
84
+ default: º
85
+ 1: º
86
+ 2: º
87
+ 3: º
88
+ 11: º
89
+ 12: º
90
+ 13: º
91
+ on_weekends: nos finais de semana
92
+ on_weekdays: nos dias úteis
93
+ days_on:
94
+ - Domingos
95
+ - Segundas-feiras
96
+ - Terças-feiras
97
+ - Quartas-feiras
98
+ - Quintas-feiras
99
+ - Sextas-feiras
100
+ - Sábados
101
+ on_days: no dia %{days}
102
+ array:
103
+ last_word_connector: ' e '
104
+ two_words_connector: ' e '
105
+ words_connector: ', '
106
+ string:
107
+ format:
108
+ day: '%{rest} %{current}'
109
+ day_of_week: '%{rest} %{current}'
110
+ day_of_month: '%{rest} %{current}'
111
+ day_of_year: '%{rest} %{current}'
112
+ hour_of_day: '%{rest} %{current}'
113
+ minute_of_hour: '%{rest} %{current}'
114
+ until: '%{rest} %{current}'
115
+ count: '%{rest} %{current}'
116
+ default: '%{rest} %{current}'
117
+
118
+ date:
119
+ abbr_day_names:
120
+ - Dom
121
+ - Seg
122
+ - Ter
123
+ - Qua
124
+ - Qui
125
+ - Sex
126
+ - Sáb
127
+ abbr_month_names:
128
+ -
129
+ - Jan
130
+ - Fev
131
+ - Mar
132
+ - Abr
133
+ - Mai
134
+ - Jun
135
+ - Jul
136
+ - Ago
137
+ - Set
138
+ - Out
139
+ - Nov
140
+ - Dez
141
+ day_names:
142
+ - Domingo
143
+ - Segunda-feira
144
+ - Terça-feira
145
+ - Quarta-feira
146
+ - Quinta-feira
147
+ - Sexta-feira
148
+ - Sábado
149
+ formats:
150
+ default: "%d/%m/%Y"
151
+ long: "%d de %B de %Y"
152
+ short: "%d de %B"
153
+ month_names:
154
+ -
155
+ - Janeiro
156
+ - Fevereiro
157
+ - Março
158
+ - Abril
159
+ - Maio
160
+ - Junho
161
+ - Julho
162
+ - Agosto
163
+ - Setembro
164
+ - Outubro
165
+ - Novembro
166
+ - Dezembro
167
+ order:
168
+ - :day
169
+ - :month
170
+ - :year
171
+
172
+ time:
173
+ am: ''
174
+ formats:
175
+ default: "%a, %d de %B de %Y, %H:%M:%S %z"
176
+ long: "%d de %B de %Y, %H:%M"
177
+ short: "%d de %B, %H:%M"
178
+ pm: ''
@@ -127,7 +127,7 @@ sv:
127
127
  - Aug
128
128
  - Sep
129
129
  - Okt
130
- - Mov
130
+ - Nov
131
131
  - Dec
132
132
  day_names:
133
133
  - Söndag
data/lib/ice_cube/i18n.rb CHANGED
@@ -1,24 +1,23 @@
1
+ require 'ice_cube/null_i18n'
2
+
1
3
  module IceCube
2
4
  module I18n
3
- def self.t(*args)
4
- backend.t(*args)
5
- end
6
5
 
7
- def self.l(*args)
8
- backend.l(*args)
6
+ LOCALES_PATH = File.expand_path(File.join('..', '..', '..', 'config', 'locales'), __FILE__)
7
+
8
+ class << self
9
+ delegate :t, :l, to: :backend
9
10
  end
10
11
 
11
12
  def self.backend
12
- @backend
13
+ @backend ||= detect_backend!
13
14
  end
14
15
 
15
16
  def self.detect_backend!
16
- require 'i18n'
17
- ::I18n.load_path += Dir[File.expand_path('../../../config/locales/*{rb,yml}', __FILE__)]
18
- @backend = ::I18n
19
- rescue LoadError
20
- require 'ice_cube/null_i18n'
21
- @backend = NullI18n
17
+ ::I18n.load_path += Dir[File.join(LOCALES_PATH, '*.yml')]
18
+ ::I18n
19
+ rescue NameError
20
+ NullI18n
22
21
  end
23
22
  end
24
23
  end
@@ -0,0 +1,89 @@
1
+ module IceCube
2
+ class InputAlignment
3
+
4
+ def initialize(rule, value, rule_part)
5
+ @rule = rule
6
+ @value = value
7
+ @rule_part = rule_part
8
+ end
9
+
10
+ attr_reader :rule, :value, :rule_part
11
+
12
+ def verify(freq, options={}, &block)
13
+ @rule.validations[:interval] or return
14
+
15
+ case @rule
16
+ when DailyRule
17
+ verify_wday_alignment(freq, &block)
18
+ when MonthlyRule
19
+ verify_month_alignment(freq, &block)
20
+ else
21
+ verify_freq_alignment(freq, &block)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def interval_validation
28
+ @interval_validation ||= @rule.validations[:interval].first
29
+ end
30
+
31
+ def interval_value
32
+ @interval_value ||= (rule_part == :interval) ? value : interval_validation.interval
33
+ end
34
+
35
+ def fixed_validations
36
+ @fixed_validations ||= @rule.validations.values.flatten.select { |v|
37
+ interval_type = (v.type == :wday ? :day : v.type)
38
+ v.class < Validations::FixedValue &&
39
+ interval_type == rule.base_interval_validation.type
40
+ }
41
+ end
42
+
43
+ def verify_freq_alignment(freq)
44
+ interval_validation.type == freq or return
45
+ (last_validation = fixed_validations.min_by(&:value)) or return
46
+
47
+ alignment = (value - last_validation.value) % interval_validation.interval
48
+ return if alignment.zero?
49
+
50
+ validation_values = fixed_validations.map(&:value).join(', ')
51
+ if rule_part == :interval
52
+ message = "interval(#{value}) " \
53
+ "must be a multiple of " \
54
+ "intervals in #{last_validation.key}(#{validation_values})"
55
+ else
56
+ message = "intervals in #{last_validation.key}(#{validation_values}, #{value}) " \
57
+ "must be multiples of " \
58
+ "interval(#{interval_validation.interval})"
59
+ end
60
+
61
+ yield ArgumentError.new(message)
62
+ end
63
+
64
+ def verify_month_alignment(_freq)
65
+ return if interval_value == 1 || (interval_value % 12).zero?
66
+ return if fixed_validations.empty?
67
+
68
+ message = "month_of_year can only be used with interval(1) or multiples of interval(12)"
69
+
70
+ yield ArgumentError.new(message)
71
+ end
72
+
73
+ def verify_wday_alignment(freq)
74
+ return if interval_value == 1
75
+
76
+ if freq == :wday
77
+ return if (interval_value % 7).zero?
78
+ return if Array(@rule.validations[:day]).empty?
79
+ message = "day can only be used with multiples of interval(7)"
80
+ else
81
+ (fixed_validation = fixed_validations.first) or return
82
+ message = "#{fixed_validation.key} can only be used with interval(1)"
83
+ end
84
+
85
+ yield ArgumentError.new(message)
86
+ end
87
+
88
+ end
89
+ end
@@ -7,13 +7,19 @@ module IceCube
7
7
 
8
8
  base = base[options[:count] == 1 ? "one" : "other"] if options[:count]
9
9
 
10
- if base.is_a?(Hash)
11
- return base.each_with_object({}) do |(key, value), hash|
12
- hash[key.is_a?(String) ? key.to_sym : key] = value
10
+ case base
11
+ when Hash
12
+ base.each_with_object({}) do |(k, v), hash|
13
+ hash[k.is_a?(String) ? k.to_sym : k] = v
13
14
  end
15
+ when Array
16
+ base.each_with_index.each_with_object({}) do |(v, k), hash|
17
+ hash[k] = v
18
+ end
19
+ else
20
+ return base unless base.include?('%{')
21
+ base % options
14
22
  end
15
-
16
- options.reduce(base) { |result, (find, replace)| result.gsub("%{#{find}}", "#{replace}") }
17
23
  end
18
24
 
19
25
  def self.l(date_or_time, options = {})
@@ -22,7 +28,7 @@ module IceCube
22
28
  end
23
29
 
24
30
  def self.config
25
- @config ||= YAML.load(File.read(File.join(File.dirname(__FILE__), '..', '..', 'config', 'locales', 'en.yml')))['en']
31
+ @config ||= YAML.load_file(File.join(IceCube::I18n::LOCALES_PATH, 'en.yml'))['en']
26
32
  end
27
33
  end
28
34
  end
@@ -1,4 +1,3 @@
1
- require 'forwardable'
2
1
  require 'delegate'
3
2
 
4
3
  module IceCube
@@ -20,18 +19,16 @@ module IceCube
20
19
  # Time.now - Occurrence.new(start_time) # => 3600
21
20
  #
22
21
  class Occurrence < SimpleDelegator
22
+ include Comparable
23
23
 
24
24
  # Report class name as 'Time' to thwart type checking.
25
25
  def self.name
26
26
  'Time'
27
27
  end
28
28
 
29
- # Optimize for common methods to avoid method_missing
30
- extend Forwardable
31
- def_delegators :start_time, :to_i, :<=>, :==
32
- def_delegators :to_range, :cover?, :include?, :each, :first, :last
33
-
34
29
  attr_reader :start_time, :end_time
30
+ alias first start_time
31
+ alias last end_time
35
32
 
36
33
  def initialize(start_time, end_time=nil)
37
34
  @start_time = start_time
@@ -39,29 +36,34 @@ module IceCube
39
36
  __setobj__ @start_time
40
37
  end
41
38
 
39
+ def to_i
40
+ @start_time.to_i
41
+ end
42
+
43
+ def <=>(other)
44
+ @start_time <=> other
45
+ end
46
+
42
47
  def is_a?(klass)
43
48
  klass == ::Time || super
44
49
  end
45
50
  alias_method :kind_of?, :is_a?
46
51
 
47
- def intersects? other
48
- if other.is_a?(Occurrence) || other.is_a?(Range)
49
- lower_bound_1 = first + 1
50
- upper_bound_1 = last # exclude end
51
- lower_bound_2 = other.first + 1
52
- upper_bound_2 = other.last + 1
53
- if (lower_bound_2 <=> upper_bound_2) > 0
54
- false
55
- elsif (lower_bound_1 <=> upper_bound_1) > 0
56
- false
57
- else
58
- (upper_bound_1 <=> lower_bound_2) >= 0 and
59
- (upper_bound_2 <=> lower_bound_1) >= 0
60
- end
61
- else
62
- cover? other
63
- end
52
+ def intersects?(other)
53
+ return cover?(other) unless other.is_a?(Occurrence) || other.is_a?(Range)
54
+
55
+ this_start = first + 1
56
+ this_end = last # exclude end boundary
57
+ other_start = other.first + 1
58
+ other_end = other.last + 1
59
+
60
+ !(this_end < other_start || this_start > other_end)
61
+ end
62
+
63
+ def cover?(other)
64
+ to_range.cover?(other)
64
65
  end
66
+ alias_method :include?, :cover?
65
67
 
66
68
  def comparable_time
67
69
  start_time
@@ -4,15 +4,18 @@ module IceCube
4
4
  data = {}
5
5
  ical_string.each_line do |line|
6
6
  (property, value) = line.split(':')
7
- (property, tzid) = property.split(';')
7
+ (property, _tzid) = property.split(';')
8
8
  case property
9
9
  when 'DTSTART'
10
- data[:start_time] = Time.parse(value)
10
+ data[:start_time] = TimeUtil.deserialize_time(value)
11
11
  when 'DTEND'
12
- data[:end_time] = Time.parse(value)
12
+ data[:end_time] = TimeUtil.deserialize_time(value)
13
+ when 'RDATE'
14
+ data[:rtimes] ||= []
15
+ data[:rtimes] += value.split(',').map { |v| TimeUtil.deserialize_time(v) }
13
16
  when 'EXDATE'
14
17
  data[:extimes] ||= []
15
- data[:extimes] += value.split(',').map{|v| Time.parse(v)}
18
+ data[:extimes] += value.split(',').map { |v| TimeUtil.deserialize_time(v) }
16
19
  when 'DURATION'
17
20
  data[:duration] # FIXME
18
21
  when 'RRULE'
@@ -41,7 +44,7 @@ module IceCube
41
44
  when 'COUNT'
42
45
  params[:count] = value.to_i
43
46
  when 'UNTIL'
44
- params[:until] = Time.parse(value).utc
47
+ params[:until] = TimeUtil.deserialize_time(value).utc
45
48
  when 'WKST'
46
49
  params[:week_start] = TimeUtil.ical_day_to_symbol(value)
47
50
  when 'BYSECOND'
data/lib/ice_cube/rule.rb CHANGED
@@ -19,20 +19,17 @@ module IceCube
19
19
  until_time || occurrence_count
20
20
  end
21
21
 
22
- def ==(rule)
23
- if rule.is_a? Rule
24
- hash = to_hash
25
- hash && hash == rule.to_hash
26
- end
22
+ def ==(other)
23
+ return false unless other.is_a? Rule
24
+ hash == other.hash
27
25
  end
28
26
 
29
27
  def hash
30
- h = to_hash
31
- h.nil? ? super : h.hash
28
+ to_hash.hash
32
29
  end
33
30
 
34
31
  def to_ical
35
- raise MethodNotImplemented, "Expected to be overrridden by subclasses"
32
+ raise MethodNotImplemented, "Expected to be overridden by subclasses"
36
33
  end
37
34
 
38
35
  # Convert from ical string and create a rule
@@ -61,11 +58,6 @@ module IceCube
61
58
  next_time(time, schedule, time).to_i == time.to_i
62
59
  end
63
60
 
64
- # Whether this rule requires a full run
65
- def full_required?
66
- !@count.nil?
67
- end
68
-
69
61
  class << self
70
62
 
71
63
  # Convert from a hash and create a rule
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class DailyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ include Validations::DayOfMonth
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ # include Validations::DayOfYear # n/a
13
+
5
14
  include Validations::DailyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class HourlyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ include Validations::DayOfMonth
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ include Validations::DayOfYear
13
+
5
14
  include Validations::HourlyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class MinutelyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ include Validations::DayOfMonth
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ include Validations::DayOfYear
13
+
5
14
  include Validations::MinutelyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class MonthlyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ include Validations::DayOfMonth
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ # include Validations::DayOfYear # n/a
13
+
5
14
  include Validations::MonthlyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class SecondlyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ include Validations::DayOfMonth
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ include Validations::DayOfYear
13
+
5
14
  include Validations::SecondlyInterval
6
15
 
7
16
  def initialize(interval = 1)
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class WeeklyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ # include Validations::DayOfMonth # n/a
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ # include Validations::DayOfYear # n/a
13
+
5
14
  include Validations::WeeklyInterval
6
15
 
7
16
  attr_reader :week_start
@@ -13,25 +22,37 @@ module IceCube
13
22
  reset
14
23
  end
15
24
 
16
- # Calculate the effective start time for when the given start time is later
17
- # in the week than one of the weekday validations, such that times could be
18
- # missed by a 7-day jump using the weekly interval, or when selecting from a
19
- # date that is misaligned from the schedule interval.
25
+ # Move the effective start time to correct for when the schedule has
26
+ # validations earlier in the week than the selected start time, e.g.
27
+ #
28
+ # Schedule.new(wednesday).weekly(2).day(:monday)
29
+ #
30
+ # The effective start time gets realigned to the second next Monday, jumping
31
+ # over the gap week for the interval (2). Without realignment, the correct
32
+ # Monday occurrence would be missed when the schedule performs a 7-day jump
33
+ # into the next interval week, arriving on the Wednesday. This corrects any
34
+ # selections from dates that are misaligned to the schedule interval.
20
35
  #
21
36
  def realign(step_time, start_time)
22
37
  time = TimeUtil::TimeWrapper.new(start_time)
23
38
  offset = wday_offset(step_time, start_time)
24
- time.add(:day, offset) if offset
25
- time.to_time
39
+ time.add(:day, offset)
40
+ super step_time, time.to_time
26
41
  end
27
42
 
43
+ # Calculate how many days to the first wday validation in the correct
44
+ # interval week. This may move backwards within the week if starting in an
45
+ # interval week with earlier validations.
46
+ #
28
47
  def wday_offset(step_time, start_time)
48
+ return 0 if step_time == start_time
49
+
29
50
  wday_validations = other_interval_validations.select { |v| v.type == :wday }
30
- return if wday_validations.none?
51
+ return 0 if wday_validations.none?
31
52
 
32
- days = (step_time - start_time).to_i / ONE_DAY
53
+ days = step_time.to_date - start_time.to_date
33
54
  interval = base_interval_validation.validate(step_time, start_time).to_i
34
- min_wday = TimeUtil.normalize_wday(wday_validations.min_by(&:day).day, week_start)
55
+ min_wday = wday_validations.map { |v| TimeUtil.normalize_wday(v.day, week_start) }.min
35
56
  step_wday = TimeUtil.normalize_wday(step_time.wday, week_start)
36
57
 
37
58
  days + interval - step_wday + min_wday
@@ -2,6 +2,15 @@ module IceCube
2
2
 
3
3
  class YearlyRule < ValidatedRule
4
4
 
5
+ include Validations::HourOfDay
6
+ include Validations::MinuteOfHour
7
+ include Validations::SecondOfMinute
8
+ include Validations::DayOfMonth
9
+ include Validations::DayOfWeek
10
+ include Validations::Day
11
+ include Validations::MonthOfYear
12
+ include Validations::DayOfYear
13
+
5
14
  include Validations::YearlyInterval
6
15
 
7
16
  def initialize(interval = 1)