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 +4 -4
- data/lib/rrule.rb +1 -0
- data/lib/rrule/context.rb +4 -4
- data/lib/rrule/frequencies/frequency.rb +4 -2
- data/lib/rrule/frequencies/simple_weekly.rb +14 -1
- data/lib/rrule/generators/all_occurrences.rb +2 -19
- data/lib/rrule/generators/by_set_position.rb +4 -15
- data/lib/rrule/generators/generator.rb +34 -0
- data/lib/rrule/rule.rb +26 -9
- data/rrule.gemspec +1 -1
- data/spec/frequencies/simple_weekly_spec.rb +11 -9
- data/spec/generators/generator_spec.rb +110 -0
- data/spec/rule_spec.rb +245 -2
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 524b148cf5608df2145bb75da8b5bca29867a30a
|
4
|
+
data.tar.gz: d258adb876e71ce558ffa5831d2964ddd9263e00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47e40a473aaf70514150dc11c6779cb3bb31e2d985324416df60515e678bd111fae56a2ff84529e4deab3572c477fa7cdbcfc1437f1e429db25056e1564f026b
|
7
|
+
data.tar.gz: 02ff7616862c0307a838f04eb4714fb57f9d261991199c09560ef51dd4d0c6cb223f4d2ff584d2ac94d09337b5dbd00c8a4bb96a6a52bdf1f7a02c34e680acab
|
data/lib/rrule.rb
CHANGED
@@ -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
|
|
data/lib/rrule/context.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
data/lib/rrule/rule.rb
CHANGED
@@ -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
|
-
|
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(
|
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
|
data/rrule.gemspec
CHANGED
@@ -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,
|
5
|
-
let(:frequency) { described_class.new(context, 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) {
|
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 [
|
15
|
-
expect(frequency.next_occurrences).to eql [
|
16
|
-
expect(frequency.next_occurrences).to eql [
|
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 [
|
25
|
-
expect(frequency.next_occurrences).to eql [
|
26
|
-
expect(frequency.next_occurrences).to eql [
|
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
|
data/spec/rule_spec.rb
CHANGED
@@ -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.
|
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.
|
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
|