rrule 0.4.1 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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