rrule 0.4.1 → 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +28 -0
  4. data/.travis.yml +27 -1
  5. data/Appraisals +27 -0
  6. data/CHANGELOG.md +14 -0
  7. data/Gemfile +9 -0
  8. data/Rakefile +9 -3
  9. data/gemfiles/activesupport_2.3_LTS.gemfile +16 -0
  10. data/gemfiles/activesupport_3.gemfile +16 -0
  11. data/gemfiles/activesupport_4.gemfile +15 -0
  12. data/gemfiles/activesupport_5.gemfile +15 -0
  13. data/gemfiles/activesupport_6.gemfile +15 -0
  14. data/gemfiles/activesupport_7.gemfile +15 -0
  15. data/lib/rrule/context.rb +5 -3
  16. data/lib/rrule/filters/by_month.rb +2 -0
  17. data/lib/rrule/filters/by_month_day.rb +2 -0
  18. data/lib/rrule/filters/by_week_day.rb +5 -3
  19. data/lib/rrule/filters/by_week_number.rb +2 -0
  20. data/lib/rrule/filters/by_year_day.rb +3 -1
  21. data/lib/rrule/frequencies/daily.rb +2 -0
  22. data/lib/rrule/frequencies/frequency.rb +4 -12
  23. data/lib/rrule/frequencies/monthly.rb +2 -0
  24. data/lib/rrule/frequencies/simple_weekly.rb +6 -6
  25. data/lib/rrule/frequencies/weekly.rb +2 -0
  26. data/lib/rrule/frequencies/yearly.rb +2 -0
  27. data/lib/rrule/generators/all_occurrences.rb +2 -0
  28. data/lib/rrule/generators/by_set_position.rb +2 -0
  29. data/lib/rrule/generators/generator.rb +5 -1
  30. data/lib/rrule/rule.rb +29 -37
  31. data/lib/rrule/version.rb +5 -0
  32. data/lib/rrule/weekday.rb +4 -2
  33. data/lib/rrule.rb +4 -0
  34. data/rrule.gemspec +18 -11
  35. data/scripts/benchmark.rb +3 -1
  36. metadata +30 -52
  37. data/spec/context_spec.rb +0 -261
  38. data/spec/filters/by_month_day_spec.rb +0 -35
  39. data/spec/filters/by_month_spec.rb +0 -35
  40. data/spec/filters/by_week_day_spec.rb +0 -35
  41. data/spec/filters/by_week_number_spec.rb +0 -41
  42. data/spec/filters/by_year_day_spec.rb +0 -35
  43. data/spec/frequencies/daily_spec.rb +0 -62
  44. data/spec/frequencies/monthly_spec.rb +0 -63
  45. data/spec/frequencies/simple_weekly_spec.rb +0 -32
  46. data/spec/frequencies/weekly_spec.rb +0 -92
  47. data/spec/frequencies/yearly_spec.rb +0 -54
  48. data/spec/generators/all_occurrences_spec.rb +0 -44
  49. data/spec/generators/by_set_position_spec.rb +0 -39
  50. data/spec/generators/generator_spec.rb +0 -110
  51. data/spec/rule_spec.rb +0 -2338
  52. data/spec/spec_helper.rb +0 -23
  53. data/spec/weekday_spec.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1aad8dd06524a47f1d310b7f077db21f41696bc7
4
- data.tar.gz: f2e1673c1fd07da16c47fc96e944eeabef43578b
2
+ SHA256:
3
+ metadata.gz: '007499934626da51ca670d58c30fab5efe4f88c782979eaff317f440e8f237fc'
4
+ data.tar.gz: a99135de58a44d1c71a2af4fee8721475f09c44cfd40ab21410c7cb755835784
5
5
  SHA512:
6
- metadata.gz: fa77f6132a2d47a45d8aab0a58af68a1ccd0417e22a9c2fc6ef4a2b14aef90e65040324177893773cba2b06e74373776105a19e078c5d81360f5e894b2cdec3c
7
- data.tar.gz: 4fddc6b136db1d57c8d8e89fe099820e572f13fc447647cdd7c43942a74112255c8720ad2b8c82a0cb1586a8a1bdb0e862ec12014f6f05c5982a86ce58207518
6
+ metadata.gz: b2f9bc5d989d05bebf09aafac0ded5fd2148901481b15fce0b4ea60680cb26fefe4f6bfebe286473a02f25543e8be499b842020988da87226d6da3eaf847070f
7
+ data.tar.gz: 5b450cdde1f0b8c7a6c05a0520ecfdcb43e93647e2155a0fc2dc1578f7fdf4457c42a638fe31fadfd8bd31a1fc006169dab6847a03fa2552c530283c91f78870
data/.gitignore CHANGED
@@ -1,4 +1,7 @@
1
+ Gemfile.lock
1
2
  .rspec
2
3
  *.gem
3
4
  *.sublime-project
4
5
  *.sublime-workspace
6
+ .ruby-version
7
+ gemfiles/*.lock
data/.rubocop.yml ADDED
@@ -0,0 +1,28 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ Exclude:
4
+ - 'gemfiles/**/*'
5
+ - Appraisals
6
+
7
+ Layout:
8
+ Enabled: false
9
+
10
+ Lint/NonLocalExitFromIterator:
11
+ Enabled: false
12
+
13
+ Metrics:
14
+ Enabled: false
15
+
16
+ Naming:
17
+ Enabled: false
18
+
19
+ Style/Documentation:
20
+ Enabled: false
21
+ Style/MultilineBlockChain:
22
+ Enabled: false
23
+ Style/NumericPredicate:
24
+ Enabled: false
25
+ Style/RescueModifier:
26
+ Enabled: false
27
+ Style/TrailingCommaInArrayLiteral:
28
+ EnforcedStyleForMultiline: comma
data/.travis.yml CHANGED
@@ -1,3 +1,29 @@
1
1
  language: ruby
2
+ sudo: false
2
3
  rvm:
3
- - 2.3.1
4
+ - 2.6.5
5
+ - 2.7.0
6
+ - 3.0.0
7
+
8
+ gemfile:
9
+ - gemfiles/activesupport_2.3_LTS.gemfile
10
+ - gemfiles/activesupport_3.gemfile
11
+ - gemfiles/activesupport_4.gemfile
12
+ - gemfiles/activesupport_5.gemfile
13
+ - gemfiles/activesupport_6.gemfile
14
+ - gemfiles/activesupport_7.gemfile
15
+
16
+ jobs:
17
+ exclude:
18
+ - rvm: 2.7.0
19
+ gemfile: gemfiles/activesupport_3.gemfile
20
+ - rvm: 2.7.0
21
+ gemfile: gemfiles/activesupport_4.gemfile
22
+ - rvm: 3.0.0
23
+ gemfile: gemfiles/activesupport_3.gemfile
24
+ - rvm: 3.0.0
25
+ gemfile: gemfiles/activesupport_4.gemfile
26
+
27
+ before_install:
28
+ - yes | gem update --system --force
29
+ - gem install bundler
data/Appraisals ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ appraise 'activesupport-2.3-LTS' do
4
+ gem 'rexml'
5
+ gem 'activesupport', '>= 2', git: 'https://github.com/makandra/rails.git', branch: '2-3-lts'
6
+ end
7
+
8
+ appraise 'activesupport-3' do
9
+ gem 'activesupport', '~> 3'
10
+ gem 'tzinfo', '~> 1.2'
11
+ end
12
+
13
+ appraise 'activesupport-4' do
14
+ gem 'activesupport', '~> 4'
15
+ end
16
+
17
+ appraise 'activesupport-5' do
18
+ gem 'activesupport', '~> 5'
19
+ end
20
+
21
+ appraise 'activesupport-6' do
22
+ gem 'activesupport', '~> 6'
23
+ end
24
+
25
+ appraise 'activesupport-7' do
26
+ gem 'activesupport', '~> 7'
27
+ end
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  Change Log
2
2
  ==========
3
3
 
4
+ Version 0.4.4 *(2022-03-01)*
5
+ ----------------------------
6
+ Remove constraint on ActiveSupport version
7
+
8
+ Version 0.4.3 *(2021-08-10)*
9
+ ----------------------------
10
+ Adding support for multiple versions of ActiveSupport, up until at least ActiveSupport 6
11
+ Handle case where DTSTART is a date
12
+ Several bugfixes (fix weekday ordinal matching, BYDAY calculations with and without ordinals)
13
+
14
+ Version 0.4.2 *(2019-02-05)*
15
+ ----------------------------
16
+ Truncate the floor_date option if it's less than the dtstart option
17
+
4
18
  Version 0.4.1 *(2018-11-28)*
5
19
  ----------------------------
6
20
  Fix bug in SimpleWeekly when ENV['TZ'] was not equal to time zone of rrule
data/Gemfile CHANGED
@@ -1,5 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
5
+ gem 'pry', '~> 0.12.2'
3
6
  gem 'rake'
7
+ gem 'rspec', '~> 3.8'
8
+ gem 'rubocop', '0.63.1'
9
+
10
+ platform :mri do
11
+ gem 'pry-byebug'
12
+ end
4
13
 
5
14
  gemspec
data/Rakefile CHANGED
@@ -1,12 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rubygems'
2
4
  require 'rake'
5
+ require 'bundler/gem_tasks'
3
6
  require 'rspec/core/rake_task'
4
- require File.expand_path('../lib/rrule', __FILE__)
7
+ require File.expand_path('lib/rrule', __dir__)
5
8
 
6
9
  RSpec::Core::RakeTask.new(:spec)
7
10
 
11
+ require 'rubocop/rake_task'
12
+ RuboCop::RakeTask.new
13
+
8
14
  namespace :spec do
9
- task :all => ['spec']
15
+ task all: ['spec']
10
16
  end
11
17
 
12
- task :default => 'spec:all'
18
+ task default: %w[spec:all rubocop]
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry", "~> 0.12.2"
6
+ gem "rake"
7
+ gem "rspec", "~> 3.8"
8
+ gem "rubocop", "0.63.1"
9
+ gem "rexml"
10
+ gem "activesupport", ">= 2", git: "https://github.com/makandra/rails.git", branch: "2-3-lts"
11
+
12
+ platforms :mri do
13
+ gem "pry-byebug"
14
+ end
15
+
16
+ gemspec path: "../"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry", "~> 0.12.2"
6
+ gem "rake"
7
+ gem "rspec", "~> 3.8"
8
+ gem "rubocop", "0.63.1"
9
+ gem "activesupport", "~> 3"
10
+ gem "tzinfo", "~> 1.2"
11
+
12
+ platforms :mri do
13
+ gem "pry-byebug"
14
+ end
15
+
16
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry", "~> 0.12.2"
6
+ gem "rake"
7
+ gem "rspec", "~> 3.8"
8
+ gem "rubocop", "0.63.1"
9
+ gem "activesupport", "~> 4"
10
+
11
+ platforms :mri do
12
+ gem "pry-byebug"
13
+ end
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry", "~> 0.12.2"
6
+ gem "rake"
7
+ gem "rspec", "~> 3.8"
8
+ gem "rubocop", "0.63.1"
9
+ gem "activesupport", "~> 5"
10
+
11
+ platforms :mri do
12
+ gem "pry-byebug"
13
+ end
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry", "~> 0.12.2"
6
+ gem "rake"
7
+ gem "rspec", "~> 3.8"
8
+ gem "rubocop", "0.63.1"
9
+ gem "activesupport", "~> 6"
10
+
11
+ platforms :mri do
12
+ gem "pry-byebug"
13
+ end
14
+
15
+ gemspec path: "../"
@@ -0,0 +1,15 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "pry", "~> 0.12.2"
6
+ gem "rake"
7
+ gem "rspec", "~> 3.8"
8
+ gem "rubocop", "0.63.1"
9
+ gem "activesupport", "~> 7"
10
+
11
+ platforms :mri do
12
+ gem "pry-byebug"
13
+ end
14
+
15
+ gemspec path: "../"
data/lib/rrule/context.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Context
3
5
  attr_reader :options, :dtstart, :tz, :day_of_year_mask, :year
@@ -17,14 +19,14 @@ module RRule
17
19
  possible_date_ranges = []
18
20
  if options[:freq] == 'YEARLY'
19
21
  if options[:bymonth]
20
- options[:bymonth].each do |month|
21
- possible_date_ranges.push(elapsed_days_in_year_by_month[(month - 1)..(month)])
22
+ options[:bymonth].each do |mon|
23
+ possible_date_ranges.push(elapsed_days_in_year_by_month[(mon - 1)..mon])
22
24
  end
23
25
  else
24
26
  possible_date_ranges = [[0, year_length_in_days]]
25
27
  end
26
28
  elsif options[:freq] == 'MONTHLY'
27
- possible_date_ranges = [elapsed_days_in_year_by_month[(month - 1)..(month)]]
29
+ possible_date_ranges = [elapsed_days_in_year_by_month[(month - 1)..month]]
28
30
  end
29
31
 
30
32
  unless possible_date_ranges.empty?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class ByMonth
3
5
  def initialize(by_months, context)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class ByMonthDay
3
5
  def initialize(by_month_days, context)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class ByWeekDay
3
5
  def initialize(weekdays, context)
@@ -6,17 +8,17 @@ module RRule
6
8
  end
7
9
 
8
10
  def reject?(i)
9
- masked?(i) || !matches_by_week_days?(i)
11
+ masked?(i) && !matches_by_week_days?(i)
10
12
  end
11
13
 
12
14
  private
13
15
 
14
16
  def masked?(i)
15
- context.day_of_year_mask && !context.day_of_year_mask[i]
17
+ context.day_of_year_mask.blank? || !context.day_of_year_mask[i]
16
18
  end
17
19
 
18
20
  def matches_by_week_days?(i)
19
- by_week_days.empty? || by_week_days.include?(context.weekday_by_day_of_year[i])
21
+ by_week_days.present? && by_week_days.include?(context.weekday_by_day_of_year[i])
20
22
  end
21
23
 
22
24
  attr_reader :by_week_days, :context
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class ByWeekNumber
3
5
  def initialize(by_week_numbers, context)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class ByYearDay
3
5
  def initialize(by_year_days, context)
@@ -6,7 +8,7 @@ module RRule
6
8
  end
7
9
 
8
10
  def reject?(i)
9
- !by_year_days.empty? &&
11
+ !by_year_days.empty? &&
10
12
  ((i < context.year_length_in_days && !by_year_days.include?(i + 1) && !by_year_days.include?(i - context.year_length_in_days)) ||
11
13
  (i >= context.year_length_in_days && !by_year_days.include?(i + 1 - context.year_length_in_days) && !by_year_days.include?(i - context.year_length_in_days - context.next_year_length_in_days)))
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Daily < Frequency
3
5
  def possible_days
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Frequency
3
5
  attr_reader :current_date, :filters, :generator, :timeset
@@ -12,9 +14,7 @@ module RRule
12
14
 
13
15
  def advance
14
16
  @current_date = current_date.advance(advance_by).tap do |new_date|
15
- unless same_month(current_date, new_date)
16
- context.rebuild(new_date.year, new_date.month)
17
- end
17
+ context.rebuild(new_date.year, new_date.month) unless same_month(current_date, new_date)
18
18
  end
19
19
  end
20
20
 
@@ -32,10 +32,6 @@ module RRule
32
32
  end
33
33
  end
34
34
 
35
- def possible_days
36
- fail NotImplementedError
37
- end
38
-
39
35
  def self.for_options(options)
40
36
  case options[:freq]
41
37
  when 'DAILY'
@@ -51,7 +47,7 @@ module RRule
51
47
  when 'YEARLY'
52
48
  Yearly
53
49
  else
54
- raise InvalidRRule, "Valid FREQ value is required"
50
+ raise InvalidRRule, 'Valid FREQ value is required'
55
51
  end
56
52
  end
57
53
 
@@ -62,9 +58,5 @@ module RRule
62
58
  def same_month(first_date, second_date)
63
59
  first_date.month == second_date.month && first_date.year == second_date.year
64
60
  end
65
-
66
- def advance_by
67
- fail NotImplementedError
68
- end
69
61
  end
70
62
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Monthly < Frequency
3
5
  def possible_days
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class SimpleWeekly < Frequency
3
5
  def next_occurrences
@@ -8,15 +10,13 @@ module RRule
8
10
  end
9
11
 
10
12
  def correct_current_date_if_needed
11
- if context.options[:byweekday].present?
12
- target_wday = context.options[:byweekday].first.index
13
+ target_wday = if context.options[:byweekday].present?
14
+ context.options[:byweekday].first.index
13
15
  else
14
- target_wday = context.dtstart.wday
16
+ context.dtstart.wday
15
17
  end
16
18
 
17
- while @current_date.wday != target_wday
18
- @current_date = @current_date + 1.day
19
- end
19
+ @current_date += 1.day while @current_date.wday != target_wday
20
20
  end
21
21
  end
22
22
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Weekly < Frequency
3
5
  def possible_days
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Yearly < Frequency
3
5
  def possible_days
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class AllOccurrences < Generator
3
5
  def combine_dates_and_times(dayset, timeset)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class BySetPosition < Generator
3
5
  attr_reader :by_set_positions
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Generator
3
5
  attr_reader :context
@@ -7,6 +9,8 @@ module RRule
7
9
  end
8
10
 
9
11
  def process_timeset(date, timeset)
12
+ return [date] if timeset.blank?
13
+
10
14
  timeset.map do |time|
11
15
  hour_sets = (
12
16
  Array.wrap(time[:hour]).sort.map do |hour|
@@ -31,4 +35,4 @@ module RRule
31
35
  end.flatten
32
36
  end
33
37
  end
34
- end
38
+ end
data/lib/rrule/rule.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Rule
3
5
  include Enumerable
@@ -6,7 +8,7 @@ module RRule
6
8
 
7
9
  def initialize(rrule, dtstart: Time.now, tzid: 'UTC', exdate: [], max_year: nil)
8
10
  @tz = tzid
9
- @dtstart = floor_to_seconds_in_timezone(dtstart)
11
+ @dtstart = dtstart.is_a?(Date) ? dtstart : floor_to_seconds_in_timezone(dtstart)
10
12
  @exdate = exdate
11
13
  @options = parse_options(rrule)
12
14
  @frequency_type = Frequency.for_options(options)
@@ -25,11 +27,8 @@ module RRule
25
27
  end
26
28
 
27
29
  def each(floor_date: nil)
28
- floor_date ||= dtstart
29
30
  # 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
31
+ floor_date = dtstart if count_or_interval_present? || floor_date.nil? || dtstart > floor_date
33
32
 
34
33
  return enum_for(:each, floor_date: floor_date) unless block_given?
35
34
  context = Context.new(options, dtstart, tz)
@@ -39,30 +38,20 @@ module RRule
39
38
  count = options[:count]
40
39
 
41
40
  filters = []
42
- if options[:bymonth]
43
- filters.push(ByMonth.new(options[:bymonth], context))
44
- end
41
+ filters.push(ByMonth.new(options[:bymonth], context)) if options[:bymonth]
45
42
 
46
- if options[:byweekno]
47
- filters.push(ByWeekNumber.new(options[:byweekno], context))
48
- end
43
+ filters.push(ByWeekNumber.new(options[:byweekno], context)) if options[:byweekno]
49
44
 
50
- if options[:byweekday]
51
- filters.push(ByWeekDay.new(options[:byweekday], context))
52
- end
45
+ filters.push(ByWeekDay.new(options[:byweekday], context)) if options[:byweekday]
53
46
 
54
- if options[:byyearday]
55
- filters.push(ByYearDay.new(options[:byyearday], context))
56
- end
47
+ filters.push(ByYearDay.new(options[:byyearday], context)) if options[:byyearday]
57
48
 
58
- if options[:bymonthday]
59
- filters.push(ByMonthDay.new(options[:bymonthday], context))
60
- end
49
+ filters.push(ByMonthDay.new(options[:bymonthday], context)) if options[:bymonthday]
61
50
 
62
- if options[:bysetpos]
63
- generator = BySetPosition.new(options[:bysetpos], context)
51
+ generator = if options[:bysetpos]
52
+ BySetPosition.new(options[:bysetpos], context)
64
53
  else
65
- generator = AllOccurrences.new(context)
54
+ AllOccurrences.new(context)
66
55
  end
67
56
 
68
57
  frequency = Frequency.for_options(options).new(context, filters, generator, timeset, start_date: floor_date)
@@ -106,7 +95,8 @@ module RRule
106
95
  def parse_options(rule)
107
96
  options = { interval: 1, wkst: 1 }
108
97
 
109
- params = rule.split(';')
98
+ # Remove RRULE: prefix to prevent parsing options incorrectly.
99
+ params = rule.delete_prefix('RRULE:').split(';')
110
100
  params.each do |param|
111
101
  option, value = param.split('=')
112
102
 
@@ -117,15 +107,17 @@ module RRule
117
107
  i = begin
118
108
  Integer(value)
119
109
  rescue ArgumentError
120
- raise InvalidRRule, "COUNT must be a non-negative integer"
110
+ raise InvalidRRule, 'COUNT must be a non-negative integer'
121
111
  end
122
- raise InvalidRRule, "COUNT must be a non-negative integer" if i < 0
112
+ raise InvalidRRule, 'COUNT must be a non-negative integer' if i < 0
123
113
  options[:count] = i
124
114
  when 'UNTIL'
125
- options[:until] = Time.parse(value)
115
+ # The value of the UNTIL rule part MUST have the same
116
+ # value type as the "DTSTART" property.
117
+ options[:until] = @dtstart.is_a?(Date) ? Date.parse(value) : Time.parse(value)
126
118
  when 'INTERVAL'
127
119
  i = Integer(value) rescue 0
128
- raise InvalidRRule, "INTERVAL must be a positive integer" unless i > 0
120
+ raise InvalidRRule, 'INTERVAL must be a positive integer' unless i > 0
129
121
  options[:interval] = i
130
122
  when 'BYHOUR'
131
123
  options[:byhour] = value.split(',').compact.map(&:to_i)
@@ -138,7 +130,7 @@ module RRule
138
130
  when 'BYSETPOS'
139
131
  options[:bysetpos] = value.split(',').map(&:to_i)
140
132
  when 'WKST'
141
- options[:wkst] = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'].index(value)
133
+ options[:wkst] = RRule::WEEKDAYS.index(value)
142
134
  when 'BYMONTH'
143
135
  options[:bymonth] = value.split(',').compact.map(&:to_i)
144
136
  when 'BYMONTHDAY'
@@ -150,12 +142,10 @@ module RRule
150
142
  end
151
143
  end
152
144
 
153
- if !(options[:byweekno] || options[:byyearday] || options[:bymonthday] || options[:byweekday])
145
+ unless options[:byweekno] || options[:byyearday] || options[:bymonthday] || options[:byweekday]
154
146
  case options[:freq]
155
147
  when 'YEARLY'
156
- unless options[:bymonth]
157
- options[:bymonth] = [dtstart.month]
158
- end
148
+ options[:bymonth] = [dtstart.month] unless options[:bymonth]
159
149
  options[:bymonthday] = [dtstart.day]
160
150
  when 'MONTHLY'
161
151
  options[:bymonthday] = [dtstart.day]
@@ -165,11 +155,13 @@ module RRule
165
155
  end
166
156
  end
167
157
 
168
- unless options[:byweekday].nil?
169
- options[:byweekday], options[:bynweekday] = options[:byweekday].partition { |wday| wday.ordinal.nil? }
170
- end
158
+ options[:byweekday], options[:bynweekday] = options[:byweekday].partition { |wday| wday.ordinal.nil? } unless options[:byweekday].nil?
171
159
 
172
- options[:timeset] = [{ hour: (options[:byhour].presence || dtstart.hour), minute: (options[:byminute].presence || dtstart.min), second: (options[:bysecond].presence || dtstart.sec) }]
160
+ # The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified
161
+ # when the associated "DTSTART" property has a DATE value type.
162
+ # These rule parts MUST be ignored in RECUR value that violate the
163
+ # above requirement
164
+ options[:timeset] = [{ hour: (options[:byhour].presence || dtstart.hour), minute: (options[:byminute].presence || dtstart.min), second: (options[:bysecond].presence || dtstart.sec) }] unless dtstart.is_a?(Date)
173
165
 
174
166
  options
175
167
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RRule
4
+ VERSION = '0.4.4'
5
+ end
data/lib/rrule/weekday.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RRule
2
4
  class Weekday
3
5
  attr_reader :index, :ordinal
@@ -8,8 +10,8 @@ module RRule
8
10
  end
9
11
 
10
12
  def self.parse(weekday)
11
- match = /([+-]?\d)?([A-Z]{2})/.match(weekday)
12
- index = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'].index(match[2])
13
+ match = /([+-]?\d+)?([A-Z]{2})/.match(weekday)
14
+ index = RRule::WEEKDAYS.index(match[2])
13
15
  ordinal = match[1] ? match[1].to_i : nil
14
16
  new(index, ordinal)
15
17
  end