rrule 0.3.1 → 0.4.0
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.
- 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
|