From 4058e6f2279e2cc3fbe60aea7fecc20489083cd8 Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 19:06:41 +0000 Subject: [PATCH 1/7] Add failing spec for issue #514 --- spec/examples/daily_rule_spec.rb | 52 ++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/spec/examples/daily_rule_spec.rb b/spec/examples/daily_rule_spec.rb index 53887774..0fff652a 100644 --- a/spec/examples/daily_rule_spec.rb +++ b/spec/examples/daily_rule_spec.rb @@ -31,44 +31,44 @@ module IceCube schedule = Schedule.new(Time.local(2013, 3, 9, 2, 30, 0)) schedule.add_recurrence_rule Rule.daily expect(schedule.first(3)).to eq([ - Time.local(2013, 3, 9, 2, 30, 0), # -0800 - Time.local(2013, 3, 10, 3, 30, 0), # -0700 - Time.local(2013, 3, 11, 2, 30, 0) # -0700 - ]) + Time.local(2013, 3, 9, 2, 30, 0), # -0800 + Time.local(2013, 3, 10, 3, 30, 0), # -0700 + Time.local(2013, 3, 11, 2, 30, 0) # -0700 + ]) end it "should not skip times in DST end hour" do schedule = Schedule.new(Time.local(2013, 11, 2, 2, 30, 0)) schedule.add_recurrence_rule Rule.daily expect(schedule.first(3)).to eq([ - Time.local(2013, 11, 2, 2, 30, 0), # -0700 - Time.local(2013, 11, 3, 2, 30, 0), # -0800 - Time.local(2013, 11, 4, 2, 30, 0) # -0800 - ]) + Time.local(2013, 11, 2, 2, 30, 0), # -0700 + Time.local(2013, 11, 3, 2, 30, 0), # -0800 + Time.local(2013, 11, 4, 2, 30, 0) # -0800 + ]) end it "should include nearest time to DST start when locking hour_of_day" do schedule = Schedule.new(Time.local(2013, 3, 9, 2, 0, 0)) schedule.add_recurrence_rule Rule.daily.hour_of_day(2) expect(schedule.first(3)).to eq([ - Time.local(2013, 3, 9, 2, 0, 0), # -0800 - Time.local(2013, 3, 10, 3, 0, 0), # -0700 - Time.local(2013, 3, 11, 2, 0, 0) # -0700 - ]) + Time.local(2013, 3, 9, 2, 0, 0), # -0800 + Time.local(2013, 3, 10, 3, 0, 0), # -0700 + Time.local(2013, 3, 11, 2, 0, 0) # -0700 + ]) end it "should not skip days where DST changes" do start_time = Time.local(2013, 3, 10, 0, 0, 0) - schedule = Schedule.new(start_time) + schedule = Schedule.new(start_time) schedule.add_recurrence_rule Rule.daily.hour_of_day(19) expect(schedule.occurrences_between(start_time, start_time + ONE_DAY)).to eq([ - Time.local(2013, 3, 10, 19, 0, 0) - ]) + Time.local(2013, 3, 10, 19, 0, 0) + ]) end end it "should update previous interval" do - t0 = Time.now + t0 = Time.now rule = Rule.daily(7) rule.interval(5) expect(rule.next_time(t0 + 1, t0, nil)).to eq(t0 + 5 * ONE_DAY) @@ -107,9 +107,23 @@ module IceCube # check assumption 2 -- 1 (2) (3) (4) 5 (6) times = schedule.occurrences(t0 + 5 * ONE_DAY) expect(times).to eq([ - t0 + 5 * ONE_HOUR + 45 * ONE_MINUTE, - t0 + 4 * ONE_DAY + 5 * ONE_HOUR + 45 * ONE_MINUTE - ]) + t0 + 5 * ONE_HOUR + 45 * ONE_MINUTE, + t0 + 4 * ONE_DAY + 5 * ONE_HOUR + 45 * ONE_MINUTE + ]) + end + + it "selects the first possible starting occurrence before starting interval" do + schedule_start = Time.new(2021, 3, 28, 10, 0, 0, 'UTC') + end_time = Time.new(2021, 4, 7, 23, 59, 59, 'UTC') + rule = IceCube::Rule.daily(4).hour_of_day(9).minute_of_hour(0).second_of_minute(0) + schedule = IceCube::Schedule.new(schedule_start) { |s| s.add_recurrence_rule(rule) } + + expect(schedule.occurrences(end_time)) + .to eq([ + Time.new(2021, 3, 29, 9, 0, 0, "UTC"), + Time.new(2021, 4, 2, 9, 0, 0, "UTC"), + Time.new(2021, 4, 6, 9, 0, 0, "UTC") + ]) end describe "day validation" do From d20d4cddf762386c23a4fb72a1e9dd828b8a1095 Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 20:57:39 +0000 Subject: [PATCH 2/7] Remove change that caused error in first occurrence selection --- lib/ice_cube/validations/hour_of_day.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ice_cube/validations/hour_of_day.rb b/lib/ice_cube/validations/hour_of_day.rb index e0a9c3be..c653cffd 100644 --- a/lib/ice_cube/validations/hour_of_day.rb +++ b/lib/ice_cube/validations/hour_of_day.rb @@ -21,7 +21,7 @@ def realign(opening_time, start_time) first_hour = Array(validations[:hour_of_day]).min_by(&:value) time = TimeUtil::TimeWrapper.new(start_time, true) - if freq > 1 && base_interval_validation.type == :hour + if freq > 1 offset = first_hour.validate(opening_time, start_time) time.add(:hour, offset - freq) else From 94eadc5d9c952f892f217495e9127b27a1b1bc69 Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 21:05:36 +0000 Subject: [PATCH 3/7] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0022b92..e59b38ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix for weekly interval results when requesting `occurrences_between` on a narrow range ([#487](https://github.com/seejohnrun/ice_cube/pull/487)) by [@jakebrady5](https://github.com/jakebrady5) - When using a rule with hour_of_day validations, and asking for occurrences on the day that DST skips forward, valid occurrences would be missed. ([#464](https://github.com/seejohnrun/ice_cube/pull/464)) by [@jakebrady5](https://github.com/jakebrady5) - Include `exrules` when exporting a schedule to YAML, JSON or a Hash. ([#519](https://github.com/ice-cube-ruby/ice_cube/pull/519)) by [@pacso](https://github.com/pacso) +- Revert [#452](https://github.com/ice-cube-ruby/ice_cube/pull/452) which resolves the issue it introduced, detailed in [#514](https://github.com/ice-cube-ruby/ice_cube/issues/514) ## [0.16.4] - 2021-10-21 ### Added From 5033b3ad78855bdb7ea456dd6f01863f5272b137 Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 21:09:37 +0000 Subject: [PATCH 4/7] Remove standard todo file --- .standard_todo.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .standard_todo.yml diff --git a/.standard_todo.yml b/.standard_todo.yml deleted file mode 100644 index 0933abc0..00000000 --- a/.standard_todo.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Auto generated files with errors to ignore. -# Remove from this list as you refactor files. ---- -ignore: -- lib/ice_cube/parsers/yaml_parser.rb: - - Security/YAMLLoad -- lib/ice_cube/rule.rb: - - Security/YAMLLoad -- spec/examples/serialization_spec.rb: - - Security/YAMLLoad -- spec/examples/to_yaml_spec.rb: - - Security/YAMLLoad From 59853d6d89827cbe78472047b201261009a10ea4 Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 21:12:11 +0000 Subject: [PATCH 5/7] Fix linting issues --- spec/examples/daily_rule_spec.rb | 57 ++++++++++++++++---------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/spec/examples/daily_rule_spec.rb b/spec/examples/daily_rule_spec.rb index 0fff652a..eb0be014 100644 --- a/spec/examples/daily_rule_spec.rb +++ b/spec/examples/daily_rule_spec.rb @@ -31,44 +31,44 @@ module IceCube schedule = Schedule.new(Time.local(2013, 3, 9, 2, 30, 0)) schedule.add_recurrence_rule Rule.daily expect(schedule.first(3)).to eq([ - Time.local(2013, 3, 9, 2, 30, 0), # -0800 - Time.local(2013, 3, 10, 3, 30, 0), # -0700 - Time.local(2013, 3, 11, 2, 30, 0) # -0700 - ]) + Time.local(2013, 3, 9, 2, 30, 0), # -0800 + Time.local(2013, 3, 10, 3, 30, 0), # -0700 + Time.local(2013, 3, 11, 2, 30, 0) # -0700 + ]) end it "should not skip times in DST end hour" do schedule = Schedule.new(Time.local(2013, 11, 2, 2, 30, 0)) schedule.add_recurrence_rule Rule.daily expect(schedule.first(3)).to eq([ - Time.local(2013, 11, 2, 2, 30, 0), # -0700 - Time.local(2013, 11, 3, 2, 30, 0), # -0800 - Time.local(2013, 11, 4, 2, 30, 0) # -0800 - ]) + Time.local(2013, 11, 2, 2, 30, 0), # -0700 + Time.local(2013, 11, 3, 2, 30, 0), # -0800 + Time.local(2013, 11, 4, 2, 30, 0) # -0800 + ]) end it "should include nearest time to DST start when locking hour_of_day" do schedule = Schedule.new(Time.local(2013, 3, 9, 2, 0, 0)) schedule.add_recurrence_rule Rule.daily.hour_of_day(2) expect(schedule.first(3)).to eq([ - Time.local(2013, 3, 9, 2, 0, 0), # -0800 - Time.local(2013, 3, 10, 3, 0, 0), # -0700 - Time.local(2013, 3, 11, 2, 0, 0) # -0700 - ]) + Time.local(2013, 3, 9, 2, 0, 0), # -0800 + Time.local(2013, 3, 10, 3, 0, 0), # -0700 + Time.local(2013, 3, 11, 2, 0, 0) # -0700 + ]) end it "should not skip days where DST changes" do start_time = Time.local(2013, 3, 10, 0, 0, 0) - schedule = Schedule.new(start_time) + schedule = Schedule.new(start_time) schedule.add_recurrence_rule Rule.daily.hour_of_day(19) expect(schedule.occurrences_between(start_time, start_time + ONE_DAY)).to eq([ - Time.local(2013, 3, 10, 19, 0, 0) - ]) + Time.local(2013, 3, 10, 19, 0, 0) + ]) end end it "should update previous interval" do - t0 = Time.now + t0 = Time.now rule = Rule.daily(7) rule.interval(5) expect(rule.next_time(t0 + 1, t0, nil)).to eq(t0 + 5 * ONE_DAY) @@ -106,24 +106,25 @@ module IceCube schedule.add_recurrence_rule Rule.daily(4).hour_of_day(5).minute_of_hour(45) # check assumption 2 -- 1 (2) (3) (4) 5 (6) times = schedule.occurrences(t0 + 5 * ONE_DAY) - expect(times).to eq([ - t0 + 5 * ONE_HOUR + 45 * ONE_MINUTE, - t0 + 4 * ONE_DAY + 5 * ONE_HOUR + 45 * ONE_MINUTE - ]) + expect(times) + .to eq([ + t0 + 5 * ONE_HOUR + 45 * ONE_MINUTE, + t0 + 4 * ONE_DAY + 5 * ONE_HOUR + 45 * ONE_MINUTE + ]) end it "selects the first possible starting occurrence before starting interval" do - schedule_start = Time.new(2021, 3, 28, 10, 0, 0, 'UTC') - end_time = Time.new(2021, 4, 7, 23, 59, 59, 'UTC') - rule = IceCube::Rule.daily(4).hour_of_day(9).minute_of_hour(0).second_of_minute(0) - schedule = IceCube::Schedule.new(schedule_start) { |s| s.add_recurrence_rule(rule) } + schedule_start = Time.new(2021, 3, 28, 10, 0, 0, "UTC") + end_time = Time.new(2021, 4, 7, 23, 59, 59, "UTC") + rule = IceCube::Rule.daily(4).hour_of_day(9).minute_of_hour(0).second_of_minute(0) + schedule = IceCube::Schedule.new(schedule_start) { |s| s.add_recurrence_rule(rule) } expect(schedule.occurrences(end_time)) .to eq([ - Time.new(2021, 3, 29, 9, 0, 0, "UTC"), - Time.new(2021, 4, 2, 9, 0, 0, "UTC"), - Time.new(2021, 4, 6, 9, 0, 0, "UTC") - ]) + Time.new(2021, 3, 29, 9, 0, 0, "UTC"), + Time.new(2021, 4, 2, 9, 0, 0, "UTC"), + Time.new(2021, 4, 6, 9, 0, 0, "UTC") + ]) end describe "day validation" do From bc951b11465df68a761cbdf65c5f8baffbebee3d Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 21:22:57 +0000 Subject: [PATCH 6/7] Use +0 rather than UTC --- spec/examples/daily_rule_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/examples/daily_rule_spec.rb b/spec/examples/daily_rule_spec.rb index eb0be014..434f12d8 100644 --- a/spec/examples/daily_rule_spec.rb +++ b/spec/examples/daily_rule_spec.rb @@ -114,16 +114,16 @@ module IceCube end it "selects the first possible starting occurrence before starting interval" do - schedule_start = Time.new(2021, 3, 28, 10, 0, 0, "UTC") - end_time = Time.new(2021, 4, 7, 23, 59, 59, "UTC") + schedule_start = Time.new(2021, 3, 28, 10, 0, 0, "+00:00") + end_time = Time.new(2021, 4, 7, 23, 59, 59, "+00:00") rule = IceCube::Rule.daily(4).hour_of_day(9).minute_of_hour(0).second_of_minute(0) schedule = IceCube::Schedule.new(schedule_start) { |s| s.add_recurrence_rule(rule) } expect(schedule.occurrences(end_time)) .to eq([ - Time.new(2021, 3, 29, 9, 0, 0, "UTC"), - Time.new(2021, 4, 2, 9, 0, 0, "UTC"), - Time.new(2021, 4, 6, 9, 0, 0, "UTC") + Time.new(2021, 3, 29, 9, 0, 0, "+00:00"), + Time.new(2021, 4, 2, 9, 0, 0, "+00:00"), + Time.new(2021, 4, 6, 9, 0, 0, "+00:00") ]) end From 118085dc8288be6ec3ee39daace9459f372fe834 Mon Sep 17 00:00:00 2001 From: Jon Pascoe Date: Wed, 2 Mar 2022 21:44:47 +0000 Subject: [PATCH 7/7] Add a matching test using `.occurrences` --- spec/examples/weekly_rule_spec.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spec/examples/weekly_rule_spec.rb b/spec/examples/weekly_rule_spec.rb index 3e8e5719..eda1d59a 100644 --- a/spec/examples/weekly_rule_spec.rb +++ b/spec/examples/weekly_rule_spec.rb @@ -357,12 +357,15 @@ module IceCube # 26 27 28 29 30 31 context "when day of start_time does not align with specified day rule" do let(:start_time) { Time.utc(2018, 8, 7, 10, 0, 0) } - let(:end_time) { Time.utc(2018, 8, 7, 15, 0, 0) } let(:biweekly) { IceCube::Rule.weekly(2).day(:saturday).hour_of_day(10) } - let(:schedule) { IceCube::Schedule.new(start_time, end_time: end_time) { |s| s.rrule biweekly } } + let(:schedule) { IceCube::Schedule.new(start_time, duration: ONE_HOUR) { |s| s.add_recurrence_rule biweekly } } let(:range) { [Time.utc(2018, 8, 11, 7, 0, 0), Time.utc(2018, 8, 12, 6, 59, 59)] } let(:expected_date) { Time.utc(2018, 8, 11, 10, 0, 0) } + it "selects the correct first occurrence" do + expect(schedule.occurrences(Time.utc(2018, 8, 12))).to match_array [expected_date] + end + it "should align to the correct day with the spans option" do expect(schedule.occurrences_between(range.first, range.last, spans: true)).to include expected_date end