rrule 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cef0ad524f55bb65033204ddb1b134d6e9fb0c6f
4
- data.tar.gz: 881e0c1e363a355b8908fb036568525e8b77dacf
3
+ metadata.gz: 524b148cf5608df2145bb75da8b5bca29867a30a
4
+ data.tar.gz: d258adb876e71ce558ffa5831d2964ddd9263e00
5
5
  SHA512:
6
- metadata.gz: e3a41b83fbd8b90734f1b9c97d493f8e4fa2e6314e300603b6cb0d9949151d60a3bcc34073a03c37e2aeece752ec99e9633f5c5f48b9f9eadb445407f3e5abb3
7
- data.tar.gz: d4a47101d49896dcc2ac9ab2108ae64c1873fc52f1bea394623508f8887d3c23748c728ffaede12a079b662f0da33256f5dd30970845e41348a12444b8b5d394
6
+ metadata.gz: 47e40a473aaf70514150dc11c6779cb3bb31e2d985324416df60515e678bd111fae56a2ff84529e4deab3572c477fa7cdbcfc1437f1e429db25056e1564f026b
7
+ data.tar.gz: 02ff7616862c0307a838f04eb4714fb57f9d261991199c09560ef51dd4d0c6cb223f4d2ff584d2ac94d09337b5dbd00c8a4bb96a6a52bdf1f7a02c34e680acab
@@ -18,6 +18,7 @@ module RRule
18
18
  autoload :ByYearDay, 'rrule/filters/by_year_day'
19
19
  autoload :ByMonthDay, 'rrule/filters/by_month_day'
20
20
 
21
+ autoload :Generator, 'rrule/generators/generator'
21
22
  autoload :AllOccurrences, 'rrule/generators/all_occurrences'
22
23
  autoload :BySetPosition, 'rrule/generators/by_set_position'
23
24
 
@@ -39,10 +39,10 @@ module RRule
39
39
  end
40
40
  end
41
41
  end
42
-
43
- @last_year = year
44
- @last_month = month
45
42
  end
43
+
44
+ @last_year = year
45
+ @last_month = month
46
46
  end
47
47
 
48
48
  def year_length_in_days
@@ -82,7 +82,7 @@ module RRule
82
82
  end
83
83
 
84
84
  def negative_week_number_by_day_of_year
85
- @negative_month_day_by_day_of_year ||= days_in_year.map { |day| day.cweek - Date.new(day.cwyear, 12, 28).cweek - 1 }
85
+ @negative_week_number_by_day_of_year ||= days_in_year.map { |day| day.cweek - Date.new(day.cwyear, 12, 28).cweek - 1 }
86
86
  end
87
87
 
88
88
  def elapsed_days_in_year_by_month
@@ -2,9 +2,9 @@ module RRule
2
2
  class Frequency
3
3
  attr_reader :current_date, :filters, :generator, :timeset
4
4
 
5
- def initialize(context, filters, generator, timeset)
5
+ def initialize(context, filters, generator, timeset, start_date: nil)
6
6
  @context = context
7
- @current_date = context.dtstart
7
+ @current_date = start_date.presence || context.dtstart
8
8
  @filters = filters
9
9
  @generator = generator
10
10
  @timeset = timeset
@@ -50,6 +50,8 @@ module RRule
50
50
  Monthly
51
51
  when 'YEARLY'
52
52
  Yearly
53
+ else
54
+ raise InvalidRRule, "Valid FREQ value is required"
53
55
  end
54
56
  end
55
57
 
@@ -1,9 +1,22 @@
1
1
  module RRule
2
2
  class SimpleWeekly < Frequency
3
3
  def next_occurrences
4
+ correct_current_date_if_needed
4
5
  this_occurrence = current_date
5
6
  @current_date += context.options[:interval].weeks
6
- [this_occurrence]
7
+ generator.process_timeset(this_occurrence, timeset)
8
+ end
9
+
10
+ def correct_current_date_if_needed
11
+ if context.options[:byweekday].present?
12
+ target_wday = context.options[:byweekday].first.index
13
+ else
14
+ target_wday = context.dtstart.wday
15
+ end
16
+
17
+ while @current_date.wday != target_wday
18
+ @current_date = @current_date + 1.day
19
+ end
7
20
  end
8
21
  end
9
22
  end
@@ -1,25 +1,8 @@
1
1
  module RRule
2
- class AllOccurrences
3
- attr_reader :context
4
-
5
- def initialize(context)
6
- @context = context
7
- end
8
-
2
+ class AllOccurrences < Generator
9
3
  def combine_dates_and_times(dayset, timeset)
10
4
  dayset.compact.map { |i| context.first_day_of_year + i }.flat_map do |date|
11
- timeset.map do |time|
12
- Time.use_zone(context.tz) do
13
- Time.zone.local(
14
- date.year,
15
- date.month,
16
- date.day,
17
- time[:hour],
18
- time[:minute],
19
- time[:second]
20
- )
21
- end
22
- end
5
+ process_timeset(date, timeset)
23
6
  end
24
7
  end
25
8
  end
@@ -1,26 +1,15 @@
1
1
  module RRule
2
- class BySetPosition
3
- attr_reader :by_set_positions, :context
2
+ class BySetPosition < Generator
3
+ attr_reader :by_set_positions
4
4
 
5
5
  def initialize(by_set_positions, context)
6
6
  @by_set_positions = by_set_positions
7
- @context = context
7
+ super(context)
8
8
  end
9
9
 
10
10
  def combine_dates_and_times(dayset, timeset)
11
11
  valid_dates(dayset).flat_map do |date|
12
- timeset.map do |time|
13
- Time.use_zone(context.tz) do
14
- Time.zone.local(
15
- date.year,
16
- date.month,
17
- date.day,
18
- time[:hour],
19
- time[:minute],
20
- time[:second]
21
- )
22
- end
23
- end
12
+ process_timeset(date, timeset)
24
13
  end
25
14
  end
26
15
 
@@ -0,0 +1,34 @@
1
+ module RRule
2
+ class Generator
3
+ attr_reader :context
4
+
5
+ def initialize(context)
6
+ @context = context
7
+ end
8
+
9
+ def process_timeset(date, timeset)
10
+ timeset.map do |time|
11
+ hour_sets = (
12
+ Array.wrap(time[:hour]).sort.map do |hour|
13
+ Array.wrap(time[:minute]).sort.map do |minute|
14
+ Array.wrap(time[:second]).sort.map{ |second| [hour, minute, second]}
15
+ end
16
+ end
17
+ ).flatten(2)
18
+
19
+ Time.use_zone(context.tz) do
20
+ hour_sets.map do |hour, minute, second|
21
+ Time.zone.local(
22
+ date.year,
23
+ date.month,
24
+ date.day,
25
+ hour,
26
+ minute,
27
+ second
28
+ )
29
+ end
30
+ end
31
+ end.flatten
32
+ end
33
+ end
34
+ end
@@ -9,6 +9,7 @@ module RRule
9
9
  @tz = tzid
10
10
  @exdate = exdate
11
11
  @options = parse_options(rrule)
12
+ @frequency_type = Frequency.for_options(options)
12
13
  @max_year = max_year || 9999
13
14
  @max_date = DateTime.new(@max_year)
14
15
  end
@@ -20,14 +21,19 @@ module RRule
20
21
  def between(start_date, end_date, limit: nil)
21
22
  floored_start_date = floor_to_seconds(start_date)
22
23
  floored_end_date = floor_to_seconds(end_date)
23
- all_until(end_date: floored_end_date, limit: limit).reject { |instance| instance < floored_start_date }
24
+ all_until(start_date: floored_start_date, end_date: floored_end_date, limit: limit).reject { |instance| instance < floored_start_date }
24
25
  end
25
26
 
26
- def each
27
- return enum_for(:each) unless block_given?
27
+ def each(floor_date: nil)
28
+ floor_date ||= dtstart
29
+ # If we have a COUNT or INTERVAL option, we have to start at dtstart, because those are relative to dtstart
30
+ if count_or_interval_present?
31
+ floor_date = dtstart
32
+ end
28
33
 
34
+ return enum_for(:each, floor_date: floor_date) unless block_given?
29
35
  context = Context.new(options, dtstart, tz)
30
- context.rebuild(dtstart.year, dtstart.month)
36
+ context.rebuild(floor_date.year, floor_date.month)
31
37
 
32
38
  timeset = options[:timeset]
33
39
  count = options[:count]
@@ -59,13 +65,14 @@ module RRule
59
65
  generator = AllOccurrences.new(context)
60
66
  end
61
67
 
62
- frequency = Frequency.for_options(options).new(context, filters, generator, timeset)
68
+ frequency = Frequency.for_options(options).new(context, filters, generator, timeset, start_date: floor_date)
63
69
 
64
70
  loop do
65
71
  return if frequency.current_date.year > max_year
66
72
 
67
73
  frequency.next_occurrences.each do |this_result|
68
74
  next if this_result < dtstart
75
+ next if floor_date.present? && this_result < floor_date
69
76
  return if options[:until] && this_result > options[:until]
70
77
  return if count && (count -= 1) < 0
71
78
  yield this_result unless exdate.include?(this_result)
@@ -79,7 +86,7 @@ module RRule
79
86
 
80
87
  private
81
88
 
82
- attr_reader :options, :max_year, :max_date
89
+ attr_reader :options, :max_year, :max_date, :frequency_type
83
90
 
84
91
  def floor_to_seconds(date)
85
92
  # This removes all sub-second and floors it to the second level.
@@ -92,8 +99,8 @@ module RRule
92
99
  @enumerator ||= to_enum
93
100
  end
94
101
 
95
- def all_until(end_date: max_date, limit: nil)
96
- limit ? take(limit) : take_while { |date| date <= end_date }
102
+ def all_until(start_date: nil, end_date: max_date, limit: nil)
103
+ limit ? take(limit) : each(floor_date: start_date).take_while { |date| date <= end_date }
97
104
  end
98
105
 
99
106
  def parse_options(rule)
@@ -120,6 +127,12 @@ module RRule
120
127
  i = Integer(value) rescue 0
121
128
  raise InvalidRRule, "INTERVAL must be a positive integer" unless i > 0
122
129
  options[:interval] = i
130
+ when 'BYHOUR'
131
+ options[:byhour] = value.split(',').compact.map(&:to_i)
132
+ when 'BYMINUTE'
133
+ options[:byminute] = value.split(',').compact.map(&:to_i)
134
+ when 'BYSECOND'
135
+ options[:bysecond] = value.split(',').compact.map(&:to_i)
123
136
  when 'BYDAY'
124
137
  options[:byweekday] = value.split(',').map { |day| Weekday.parse(day) }
125
138
  when 'BYSETPOS'
@@ -156,9 +169,13 @@ module RRule
156
169
  options[:byweekday], options[:bynweekday] = options[:byweekday].partition { |wday| wday.ordinal.nil? }
157
170
  end
158
171
 
159
- options[:timeset] = [{ hour: dtstart.hour, minute: dtstart.min, second: dtstart.sec }]
172
+ options[:timeset] = [{ hour: (options[:byhour].presence || dtstart.hour), minute: (options[:byminute].presence || dtstart.min), second: (options[:bysecond].presence || dtstart.sec) }]
160
173
 
161
174
  options
162
175
  end
176
+
177
+ def count_or_interval_present?
178
+ options[:count].present? || (options[:interval].present? && options[:interval] > 1)
179
+ end
163
180
  end
164
181
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'rrule'
3
- s.version = '0.3.1'
3
+ s.version = '0.4.0'
4
4
  s.date = '2018-04-24'
5
5
  s.summary = 'RRule expansion'
6
6
  s.description = 'A gem for expanding dates according to the RRule specification'
@@ -1,19 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe RRule::SimpleWeekly do
4
- let(:context) { RRule::Context.new({ interval: interval }, date, nil) }
5
- let(:frequency) { described_class.new(context, nil, nil, nil) }
4
+ let(:context) { RRule::Context.new({ interval: interval }, date, 'America/Los_Angeles') }
5
+ let(:frequency) { described_class.new(context, nil, generator, timeset) }
6
+ let(:generator) { RRule::AllOccurrences.new(context) }
7
+ let(:timeset) { [{ hour: date.hour, minute: date.min, second: date.sec }] }
6
8
 
7
9
  describe '#next_occurrences' do
8
- let(:date) { Date.new(1997, 1, 1) }
10
+ let(:date) { Time.zone.local(1997, 1, 1) }
9
11
 
10
12
  context 'with an interval of 1' do
11
13
  let(:interval) { 1 }
12
14
 
13
15
  it 'returns occurrences every week' do
14
- expect(frequency.next_occurrences).to eql [Date.new(1997, 1, 1)]
15
- expect(frequency.next_occurrences).to eql [Date.new(1997, 1, 8)]
16
- expect(frequency.next_occurrences).to eql [Date.new(1997, 1, 15)]
16
+ expect(frequency.next_occurrences).to eql [Time.zone.local(1997, 1, 1)]
17
+ expect(frequency.next_occurrences).to eql [Time.zone.local(1997, 1, 8)]
18
+ expect(frequency.next_occurrences).to eql [Time.zone.local(1997, 1, 15)]
17
19
  end
18
20
  end
19
21
 
@@ -21,9 +23,9 @@ describe RRule::SimpleWeekly do
21
23
  let(:interval) { 2 }
22
24
 
23
25
  it 'returns occurrences every other week' do
24
- expect(frequency.next_occurrences).to eql [Date.new(1997, 1, 1)]
25
- expect(frequency.next_occurrences).to eql [Date.new(1997, 1, 15)]
26
- expect(frequency.next_occurrences).to eql [Date.new(1997, 1, 29)]
26
+ expect(frequency.next_occurrences).to eql [Time.zone.local(1997, 1, 1)]
27
+ expect(frequency.next_occurrences).to eql [Time.zone.local(1997, 1, 15)]
28
+ expect(frequency.next_occurrences).to eql [Time.zone.local(1997, 1, 29)]
27
29
  end
28
30
  end
29
31
  end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ describe RRule::Generator do
4
+ let(:context) do
5
+ RRule::Context.new(
6
+ { interval: 1, wkst: 1 },
7
+ Time.parse('Wed Jan 1 00:00:00 PST 1997'),
8
+ 'America/Los_Angeles'
9
+ )
10
+ end
11
+
12
+ around(:each) do |example|
13
+ Time.use_zone('America/Los_Angeles') do
14
+ example.run
15
+ end
16
+ end
17
+
18
+ before(:each) { context.rebuild(1997, 1) }
19
+
20
+ describe '#process_timeset' do
21
+ describe "single timeset" do
22
+ subject { described_class.new(context).process_timeset(date, timeset)}
23
+
24
+ context 'with a timeset with only 1 set' do
25
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
26
+ let(:timeset) { [{ hour: 12, minute: 30, second: 15 }] }
27
+
28
+ it { is_expected.to match_array [Time.parse('Wed Jan 1 12:30:15 PST 1997')] }
29
+ end
30
+
31
+ context 'with multiple hours in the timeset' do
32
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
33
+ let(:timeset) { [{ hour: [12, 15], minute: 30, second: 15 }] }
34
+
35
+ it { is_expected.to match_array [Time.parse('Wed Jan 1 12:30:15 PST 1997'), Time.parse('Wed Jan 1 15:30:15 PST 1997')] }
36
+ end
37
+
38
+ context 'with multiple minutes in the timeset' do
39
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
40
+ let(:timeset) { [{ hour: [12], minute: [15, 30], second: 15 }] }
41
+
42
+ it { is_expected.to match_array [Time.parse('Wed Jan 1 12:15:15 PST 1997'), Time.parse('Wed Jan 1 12:30:15 PST 1997')] }
43
+ end
44
+
45
+ context 'with multiple seconds in the timeset' do
46
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
47
+ let(:timeset) { [{ hour: [12], minute: [30], second: [15, 59] }] }
48
+
49
+ it { is_expected.to match_array [Time.parse('Wed Jan 1 12:30:15 PST 1997'), Time.parse('Wed Jan 1 12:30:59 PST 1997')] }
50
+ end
51
+
52
+ context 'with multiple hours, minutes, and seconds in the timeset' do
53
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
54
+ let(:timeset) { [{ hour: [12, 20], minute: [30, 55], second: [15, 59] }] }
55
+
56
+ it { is_expected.to eq [
57
+ Time.parse('Wed Jan 1 12:30:15 PST 1997'),
58
+ Time.parse('Wed Jan 1 12:30:59 PST 1997'),
59
+ Time.parse('Wed Jan 1 12:55:15 PST 1997'),
60
+ Time.parse('Wed Jan 1 12:55:59 PST 1997'),
61
+ Time.parse('Wed Jan 1 20:30:15 PST 1997'),
62
+ Time.parse('Wed Jan 1 20:30:59 PST 1997'),
63
+ Time.parse('Wed Jan 1 20:55:15 PST 1997'),
64
+ Time.parse('Wed Jan 1 20:55:59 PST 1997'),
65
+ ] }
66
+ end
67
+
68
+ context 'with multiple hours, minutes, and seconds in the timeset, unsorted' do
69
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
70
+ let(:timeset) { [{ hour: [20, 12], minute: [55, 30], second: [59, 15] }] }
71
+
72
+ it { is_expected.to eq [
73
+ Time.parse('Wed Jan 1 12:30:15 PST 1997'),
74
+ Time.parse('Wed Jan 1 12:30:59 PST 1997'),
75
+ Time.parse('Wed Jan 1 12:55:15 PST 1997'),
76
+ Time.parse('Wed Jan 1 12:55:59 PST 1997'),
77
+ Time.parse('Wed Jan 1 20:30:15 PST 1997'),
78
+ Time.parse('Wed Jan 1 20:30:59 PST 1997'),
79
+ Time.parse('Wed Jan 1 20:55:15 PST 1997'),
80
+ Time.parse('Wed Jan 1 20:55:59 PST 1997'),
81
+ ] }
82
+ end
83
+ end
84
+ end
85
+
86
+ describe "multiple timesets" do
87
+ subject { described_class.new(context).process_timeset(date, timeset)}
88
+
89
+ context 'with multiple timsets' do
90
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
91
+ let(:timeset) { [{ hour: 12, minute: 30, second: 15 }, { hour: 18, minute: 45, second: 20 }] }
92
+
93
+ it { is_expected.to eq [Time.parse('Wed Jan 1 12:30:15 PST 1997'), Time.parse('Wed Jan 1 18:45:20 PST 1997')] }
94
+ end
95
+
96
+ context 'with multiple timsets with multiple hour sets' do
97
+ let(:date) { Time.parse('Wed Jan 1 00:11:22 PST 1997') }
98
+ let(:timeset) { [{ hour: [12, 20], minute: 30, second: [15, 45] }, { hour: 18, minute: [22, 45], second: 20 }] }
99
+
100
+ it { is_expected.to eq [
101
+ Time.parse('Wed Jan 1 12:30:15 PST 1997'),
102
+ Time.parse('Wed Jan 1 12:30:45 PST 1997'),
103
+ Time.parse('Wed Jan 1 20:30:15 PST 1997'),
104
+ Time.parse('Wed Jan 1 20:30:45 PST 1997'),
105
+ Time.parse('Wed Jan 1 18:22:20 PST 1997'),
106
+ Time.parse('Wed Jan 1 18:45:20 PST 1997')
107
+ ] }
108
+ end
109
+ end
110
+ end
@@ -30,6 +30,80 @@ describe RRule::Rule do
30
30
  end
31
31
  end
32
32
 
33
+ describe 'iterating with a floor_date' do
34
+ describe 'No COUNT or INTERVAL > 1' do
35
+ it 'uses the floor_date provided when iterating' do
36
+ rrule = 'FREQ=DAILY'
37
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
38
+ timezone = 'America/New_York'
39
+
40
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
41
+
42
+ floor_date = Time.parse('Mon Sep 3 06:00:00 PDT 2018')
43
+
44
+ expect(rrule.each(floor_date: floor_date).take(3)).to match_array([
45
+ Time.parse('Tue Sep 3 06:00:00 PDT 2018'),
46
+ Time.parse('Wed Sep 4 06:00:00 PDT 2018'),
47
+ Time.parse('Thu Sep 5 06:00:00 PDT 2018')
48
+ ])
49
+ end
50
+ end
51
+
52
+ describe 'COUNT present' do
53
+ it 'starts at dtstart when iterating' do
54
+ rrule = 'FREQ=DAILY;COUNT=10'
55
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
56
+ timezone = 'America/New_York'
57
+
58
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
59
+
60
+ floor_date = Time.parse('Mon Sep 3 06:00:00 PDT 2018')
61
+
62
+ expect(rrule.each(floor_date: floor_date).take(3)).to match_array([
63
+ Time.parse('Tue Sep 2 06:00:00 PDT 1997'),
64
+ Time.parse('Wed Sep 3 06:00:00 PDT 1997'),
65
+ Time.parse('Thu Sep 4 06:00:00 PDT 1997'),
66
+ ])
67
+ end
68
+ end
69
+
70
+ describe 'INTERVAL present' do
71
+ it 'starts at dtstart when iterating' do
72
+ rrule = 'FREQ=DAILY;INTERVAL=10'
73
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
74
+ timezone = 'America/New_York'
75
+
76
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
77
+
78
+ floor_date = Time.parse('Mon Sep 3 06:00:00 PDT 2018')
79
+
80
+ expect(rrule.each(floor_date: floor_date).take(3)).to match_array([
81
+ Time.parse('Tue Sep 2 06:00:00 PDT 1997'),
82
+ Time.parse('Fri Sep 12 06:00:00 PDT 1997'),
83
+ Time.parse('Mon Sep 22 06:00:00 PDT 1997'),
84
+ ])
85
+ end
86
+ end
87
+
88
+ describe 'INTERVAL AND COUNT present' do
89
+ it 'starts at dtstart when iterating' do
90
+ rrule = 'FREQ=DAILY;INTERVAL=10;COUNT=5'
91
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
92
+ timezone = 'America/New_York'
93
+
94
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
95
+
96
+ floor_date = Time.parse('Mon Sep 3 06:00:00 PDT 2018')
97
+
98
+ expect(rrule.each(floor_date: floor_date).take(3)).to match_array([
99
+ Time.parse('Tue Sep 2 06:00:00 PDT 1997'),
100
+ Time.parse('Fri Sep 12 06:00:00 PDT 1997'),
101
+ Time.parse('Mon Sep 22 06:00:00 PDT 1997'),
102
+ ])
103
+ end
104
+ end
105
+ end
106
+
33
107
  describe '#all' do
34
108
  it 'returns the correct result with an rrule of FREQ=DAILY;COUNT=10' do
35
109
  rrule = 'FREQ=DAILY;COUNT=10'
@@ -1715,6 +1789,25 @@ describe RRule::Rule do
1715
1789
  end
1716
1790
 
1717
1791
  describe '#between' do
1792
+ it 'returns the correct result with an rrule of FREQ=WEEKLY;BYSECOND=59;BYMINUTE=59;BYHOUR=23;WKST=SU' do
1793
+ rrule = 'FREQ=WEEKLY;BYSECOND=59;BYMINUTE=59;BYHOUR=23;WKST=SU'
1794
+ dtstart = DateTime.parse('2018-02-04 04:00:00 +1000')
1795
+ timezone = 'Brisbane'
1796
+
1797
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
1798
+ expect(rrule.between(Time.parse('Sun, 08 Apr 2018 00:00:00 +0000'), Time.parse('Fri, 08 Jun 2018 23:59:59 +0000'))).to match_array([
1799
+ Time.parse('Sun, 08 Apr 2018 23:59:59 +1000'),
1800
+ Time.parse('Sun, 15 Apr 2018 23:59:59 +1000'),
1801
+ Time.parse('Sun, 22 Apr 2018 23:59:59 +1000'),
1802
+ Time.parse('Sun, 29 Apr 2018 23:59:59 +1000'),
1803
+ Time.parse('Sun, 06 May 2018 23:59:59 +1000'),
1804
+ Time.parse('Sun, 13 May 2018 23:59:59 +1000'),
1805
+ Time.parse('Sun, 20 May 2018 23:59:59 +1000'),
1806
+ Time.parse('Sun, 27 May 2018 23:59:59 +1000'),
1807
+ Time.parse('Sun, 03 Jun 2018 23:59:59 +1000'),
1808
+ ])
1809
+ end
1810
+
1718
1811
  it 'returns the correct result with an rrule of FREQ=DAILY;INTERVAL=2' do
1719
1812
  rrule = 'FREQ=DAILY;INTERVAL=2'
1720
1813
  dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
@@ -1979,7 +2072,7 @@ describe RRule::Rule do
1979
2072
  ])
1980
2073
  end
1981
2074
 
1982
- it 'returns the correct result with an rrule of FREQ=DAILY;COUNT=7 when the range extends beyond the end of the recurrence' do
2075
+ it 'returns the correct result with an rrule of FREQ=DAILY;COUNT=7 when the range extends beyond the end of the recurrence (run out of COUNT before the range ends)' do
1983
2076
  rrule ='FREQ=DAILY;COUNT=7'
1984
2077
  dtstart = Time.parse('Thu Feb 6 16:00:00 PST 2014')
1985
2078
  timezone = 'America/Los_Angeles'
@@ -2002,7 +2095,7 @@ describe RRule::Rule do
2002
2095
  expect(rrule.all).to match_array([
2003
2096
  Time.parse('Tue Sep 2 06:00:00 PST 1997'),
2004
2097
  Time.parse('Wed Sep 3 06:00:00 PST 1997'),
2005
- Time.parse('Thu Sep 4 06:00:00 PST 1997"'),
2098
+ Time.parse('Thu Sep 4 06:00:00 PST 1997'),
2006
2099
  Time.parse('Sat Sep 6 06:00:00 PST 1997'),
2007
2100
  Time.parse('Sun Sep 7 06:00:00 PST 1997'),
2008
2101
  Time.parse('Tue Sep 9 06:00:00 PST 1997'),
@@ -2046,9 +2139,159 @@ describe RRule::Rule do
2046
2139
  end_time = Time.parse('Wed Aug 31 21:59:59 PDT 2016')
2047
2140
  expect(rule.between(start_time, end_time)).to eql([expected_instance])
2048
2141
  end
2142
+
2143
+
2144
+ describe 'iterating with a floor_date' do
2145
+ describe 'No COUNT or INTERVAL > 1' do
2146
+ it 'still limits to the given range' do
2147
+ rrule = 'FREQ=DAILY'
2148
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
2149
+ timezone = 'America/New_York'
2150
+
2151
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2152
+
2153
+ start_time = Time.parse('Mon Sep 3 06:00:00 PDT 2018')
2154
+ end_time = Time.parse('Thu Sep 10 06:00:00 PDT 2018')
2155
+
2156
+ expect(rrule.between(start_time, end_time).take(3)).to match_array([
2157
+ Time.parse('Tue Sep 3 06:00:00 PDT 2018'),
2158
+ Time.parse('Wed Sep 4 06:00:00 PDT 2018'),
2159
+ Time.parse('Thu Sep 5 06:00:00 PDT 2018')
2160
+ ])
2161
+ end
2162
+ end
2163
+
2164
+ describe 'COUNT present' do
2165
+ it 'still limits to the given range' do
2166
+ rrule = 'FREQ=DAILY;COUNT=10'
2167
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
2168
+ timezone = 'America/New_York'
2169
+
2170
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2171
+
2172
+ start_time = Time.parse('Wed Sep 3 06:00:00 PDT 1997')
2173
+ end_time = Time.parse('Thu Sep 10 06:00:00 PDT 2018')
2174
+
2175
+ expect(rrule.between(start_time, end_time).take(3)).to match_array([
2176
+ Time.parse('Tue Sep 3 06:00:00 PDT 1997'),
2177
+ Time.parse('Wed Sep 4 06:00:00 PDT 1997'),
2178
+ Time.parse('Thu Sep 5 06:00:00 PDT 1997'),
2179
+ ])
2180
+ end
2181
+ end
2182
+
2183
+ describe 'INTERVAL present' do
2184
+ it 'still limits to the given range' do
2185
+ rrule = 'FREQ=DAILY;INTERVAL=10'
2186
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
2187
+ timezone = 'America/New_York'
2188
+
2189
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2190
+
2191
+ start_time = Time.parse('Wed Sep 3 06:00:00 PDT 1997')
2192
+ end_time = Time.parse('Thu Sep 30 06:00:00 PDT 2018')
2193
+
2194
+ expect(rrule.between(start_time, end_time).take(3)).to match_array([
2195
+ Time.parse('Tue Sep 12 06:00:00 PDT 1997'),
2196
+ Time.parse('Fri Sep 22 06:00:00 PDT 1997'),
2197
+ Time.parse('Mon Oct 02 06:00:00 PDT 1997'),
2198
+ ])
2199
+ end
2200
+ end
2201
+
2202
+ describe 'INTERVAL AND COUNT present' do
2203
+ it 'still limits to the given range' do
2204
+ rrule = 'FREQ=DAILY;INTERVAL=10;COUNT=5'
2205
+ dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
2206
+ timezone = 'America/New_York'
2207
+
2208
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2209
+
2210
+ start_time = Time.parse('Wed Sep 3 06:00:00 PDT 1997')
2211
+ end_time = Time.parse('Thu Sep 30 06:00:00 PDT 2018')
2212
+
2213
+ expect(rrule.between(start_time, end_time).take(3)).to match_array([
2214
+ Time.parse('Tue Sep 12 06:00:00 PDT 1997'),
2215
+ Time.parse('Fri Sep 22 06:00:00 PDT 1997'),
2216
+ Time.parse('Mon Oct 02 06:00:00 PDT 1997'),
2217
+ ])
2218
+ end
2219
+ end
2220
+ end
2221
+ end
2222
+
2223
+ it 'returns the correct result with an rrule of FREQ=WEEKLY;BYMONTH=1,3;COUNT=4;BYHOUR=2' do
2224
+ rrule = 'FREQ=WEEKLY;BYMONTH=1,3;COUNT=4;BYHOUR=2'
2225
+ dtstart = Time.parse('Tue Sep 2 09:00:00 PDT 1997')
2226
+ timezone = 'America/Los_Angeles'
2227
+
2228
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2229
+
2230
+ expect(rrule.all).to match_array([
2231
+ Time.parse('Tue Jan 6 02:00:00 PST 1998'),
2232
+ Time.parse('Tue Jan 13 02:00:00 PST 1998'),
2233
+ Time.parse('Tue Jan 20 02:00:00 PST 1998'),
2234
+ Time.parse('Tue Jan 27 02:00:00 PST 1998')
2235
+ ])
2236
+ end
2237
+
2238
+ it 'returns the correct result with an rrule of FREQ=WEEKLY;BYMONTH=1,3;COUNT=4;BYHOUR=2;BYMINUTE=44' do
2239
+ rrule = 'FREQ=WEEKLY;BYMONTH=1,3;COUNT=4;BYHOUR=2;BYMINUTE=44'
2240
+ dtstart = Time.parse('Tue Sep 2 09:00:00 PDT 1997')
2241
+ timezone = 'America/Los_Angeles'
2242
+
2243
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2244
+ expect(rrule.all).to match_array([
2245
+ Time.parse('Tue Jan 6 02:44:00 PST 1998'),
2246
+ Time.parse('Tue Jan 13 02:44:00 PST 1998'),
2247
+ Time.parse('Tue Jan 20 02:44:00 PST 1998'),
2248
+ Time.parse('Tue Jan 27 02:44:00 PST 1998')
2249
+ ])
2250
+ end
2251
+
2252
+ it 'returns the correct result with an rrule of FREQ=WEEKLY;BYMONTH=1,3;COUNT=24;BYHOUR=2,4,6;BYMINUTE=33,22' do
2253
+ rrule = 'FREQ=WEEKLY;BYMONTH=1,3;COUNT=24;BYHOUR=2,4,6;BYMINUTE=33,22'
2254
+ dtstart = Time.parse('Tue Sep 2 09:23:42 PDT 1997')
2255
+ timezone = 'America/Los_Angeles'
2256
+
2257
+ rrule = RRule::Rule.new(rrule, dtstart: dtstart, tzid: timezone)
2258
+ expect(rrule.all).to match_array([
2259
+ Time.parse('Tue Jan 6 02:22:42 PST 1998'),
2260
+ Time.parse('Tue Jan 6 02:33:42 PST 1998'),
2261
+ Time.parse('Tue Jan 6 04:22:42 PST 1998'),
2262
+ Time.parse('Tue Jan 6 04:33:42 PST 1998'),
2263
+ Time.parse('Tue Jan 6 06:22:42 PST 1998'),
2264
+ Time.parse('Tue Jan 6 06:33:42 PST 1998'),
2265
+ Time.parse('Tue Jan 13 02:22:42 PST 1998'),
2266
+ Time.parse('Tue Jan 13 02:33:42 PST 1998'),
2267
+ Time.parse('Tue Jan 13 04:22:42 PST 1998'),
2268
+ Time.parse('Tue Jan 13 04:33:42 PST 1998'),
2269
+ Time.parse('Tue Jan 13 06:22:42 PST 1998'),
2270
+ Time.parse('Tue Jan 13 06:33:42 PST 1998'),
2271
+ Time.parse('Tue Jan 20 02:22:42 PST 1998'),
2272
+ Time.parse('Tue Jan 20 02:33:42 PST 1998'),
2273
+ Time.parse('Tue Jan 20 04:22:42 PST 1998'),
2274
+ Time.parse('Tue Jan 20 04:33:42 PST 1998'),
2275
+ Time.parse('Tue Jan 20 06:22:42 PST 1998'),
2276
+ Time.parse('Tue Jan 20 06:33:42 PST 1998'),
2277
+ Time.parse('Tue Jan 27 02:22:42 PST 1998'),
2278
+ Time.parse('Tue Jan 27 02:33:42 PST 1998'),
2279
+ Time.parse('Tue Jan 27 04:22:42 PST 1998'),
2280
+ Time.parse('Tue Jan 27 04:33:42 PST 1998'),
2281
+ Time.parse('Tue Jan 27 06:22:42 PST 1998'),
2282
+ Time.parse('Tue Jan 27 06:33:42 PST 1998')
2283
+ ])
2049
2284
  end
2050
2285
 
2051
2286
  describe 'validation' do
2287
+ it 'raises RRule::InvalidRRule if FREQ is not provided' do
2288
+ expect { RRule::Rule.new('') }.to raise_error(RRule::InvalidRRule)
2289
+ expect { RRule::Rule.new('FREQ=') }.to raise_error(RRule::InvalidRRule)
2290
+ expect { RRule::Rule.new('FREQ=FOO') }.to raise_error(RRule::InvalidRRule)
2291
+ expect { RRule::Rule.new('COUNT=1') }.to raise_error(RRule::InvalidRRule)
2292
+ expect { RRule::Rule.new('FREQ=FOO;COUNT=1') }.to raise_error(RRule::InvalidRRule)
2293
+ end
2294
+
2052
2295
  it 'raises RRule::InvalidRRule if INTERVAL is not a positive integer' do
2053
2296
  dtstart = Time.parse('Tue Sep 2 06:00:00 PDT 1997')
2054
2297
  timezone = 'America/New_York'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rrule
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Mitchell
@@ -67,6 +67,7 @@ files:
67
67
  - lib/rrule/frequencies/yearly.rb
68
68
  - lib/rrule/generators/all_occurrences.rb
69
69
  - lib/rrule/generators/by_set_position.rb
70
+ - lib/rrule/generators/generator.rb
70
71
  - lib/rrule/rule.rb
71
72
  - lib/rrule/weekday.rb
72
73
  - rrule.gemspec
@@ -85,6 +86,7 @@ files:
85
86
  - spec/frequencies/yearly_spec.rb
86
87
  - spec/generators/all_occurrences_spec.rb
87
88
  - spec/generators/by_set_position_spec.rb
89
+ - spec/generators/generator_spec.rb
88
90
  - spec/rule_spec.rb
89
91
  - spec/spec_helper.rb
90
92
  - spec/weekday_spec.rb
@@ -107,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
109
  version: '0'
108
110
  requirements: []
109
111
  rubyforge_project:
110
- rubygems_version: 2.5.2
112
+ rubygems_version: 2.6.13
111
113
  signing_key:
112
114
  specification_version: 4
113
115
  summary: RRule expansion
@@ -125,6 +127,7 @@ test_files:
125
127
  - spec/frequencies/yearly_spec.rb
126
128
  - spec/generators/all_occurrences_spec.rb
127
129
  - spec/generators/by_set_position_spec.rb
130
+ - spec/generators/generator_spec.rb
128
131
  - spec/rule_spec.rb
129
132
  - spec/spec_helper.rb
130
133
  - spec/weekday_spec.rb