ardavis
ardavis

Reputation: 9895

Event Recurrence with Time Zones using the Ice Cube gem

How do I utilize the ice_cube gem for items (events) that have both a start and end time (Ex: Mon, Wed, Fri @ 10pm-11:30pm EST)?

I currently store all times in the database in UTC. So for the example above, the item looks like this:

#<Item recurring: {:validations=>{:day=>[1, 3, 5]}, :rule_type=>"IceCube::WeeklyRule", :interval=>1, :week_start=>0}, start_time: "2017-11-21 03:00:00", end_time: "2017-12-24 04:30:00"">

If I want to see if that item occurs on a date between two times, I''m attempting this:

item.schedule(item.start_time).occurs_between?(some_start_time, some_end_time)

This unfortunately returns false if I pass in some_start_time of Tue, 28 Nov 2017 03:00:00 UTC +00:00 (Mon @ 10pm EST) and some_end_time of Tue, 28 Nov 2017 04:30:00 UTC +00:00 (Mon @ 11:30 EST). I believe that's because the ice_cube gem will only show conflicts for Mon, Wed, Fri with the rules I've setup?

I feel like I'm missing something fundamental about the gem. How do I account for time zones properly? I think the ultimate goal would be, if I let the user select Mon, Wed, Fri from 10pm to 11:30pm, I need it to store the recurrence as Tue, Thu, Sat from 3am to 4:30am.

Here's how I'm implementing my Item class in Rails.

class Item
  serialize :recurring, Hash

  def recurring=(value)
    RecurringSelect.is_valid_rule?(value) ? super(RecurringSelect.dirty_hash_to_rule(value).to_hash) : super(nil)
  end

  def rule
    IceCube::Rule.from_hash(recurring)
  end

  def schedule(start)
    schedule = IceCube::Schedule.new(start)
    schedule.add_recurrence_rule(rule)
    schedule
  end
end

Update

I am also using the recurrence_select gem. This is what let's the user easily choose "Weekly on Monday, Wednesday, and Friday". I separately let the user select the start_time and end_time for the item. I'm correctly converting the times to UTC from their time zone when I save to the database. I somehow need to know if the days roll over as well and modify them before it saves.

Upvotes: 1

Views: 728

Answers (1)

inveterateliterate
inveterateliterate

Reputation: 369

TL/DR; Yes, since your rule says M, W, F you will get false when you pass in Tuesday.

More info:

From the documentation, "A schedule's occurrences will be returned in the same class and time zone as the schedule's start_time." If you pass UTC, it will return in UTC (or whatever timezone you choose).

Timezones are a pain, but there are several Rails helpers to get you on the right track (http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html). Your instinct to save everything to the db in UTC is a good one. The trick then is keeping track of the days. Essentially you need to have a way to have a display for the user that makes sense in their timezone, then translate it the db in UTC, and pass it to your rule appropriately.

Take a look at this helper, which allows you to convert between timezones, and even pass in a date argument to refine which date you want the actual time attached to: https://github.com/LaunchPadLab/rails_util/blob/master/lib/rails_util/timezone_helper.rb

Not trying to promote, you don't have to use that library, but it might be worth taking a look at the logic we came up with, and see if you can apply that thinking to your app.

Upvotes: 1

Related Questions