There must be something simple being overlooked here...
I've been trying various methods for creating a basic IceCube
schedule (https://github.com/seejohnrun/ice_cube). The overall goal is to use IceCube
to allow "price schedules" inside a "room reservation" rails application.
The first scenario would be creating a basic schedule
with a specific start_time
and end_time
- occurring only once. IceCube
can do this, correct?
The schedule would begin on the start_time
and end at the end_time
. I would expect to be able to check if dates or times occurs_on?
this schedule to determine if a room price should be adjusted.
So in console I've tried creating a basic schedule and would expect it to be occurring 5.days
from now since the start_time
is Time.now
and the end_time
is Time.now + 30.days
. But it seems to never return true...
1.8.7 :001 > schedule = IceCube::Schedule.new(Time.now, :end_time => Time.now + 30.days)
=> #<IceCube::Schedule:0xb619d604 @all_recurrence_rules=[], @duration=nil, @end_time=Tue Jan 08 09:13:11 -0600 2013, @all_exception_rules=[], @start_time=Sun Dec 09 09:13:11 -0600 2012>
1.8.7 :002 > schedule.occurs_on? Date.today + 5.days
=> false
1.8.7 :005 > schedule.occurs_at? Time.now + 5.days
=> false
1.8.7 :006 > schedule.occurring_at? Time.now + 5.days
=> false
Adding a recurrence rule
1.8.7 :018 > schedule.rrule IceCube::Rule.monthly
=> [#<IceCube::MonthlyRule:0xb687a88c @validations={:base_day=>[#<IceCube::Validations::ScheduleLock::Validation:0xb6875b0c @type=:day>], :base_hour=>[#<IceCube::Validations::ScheduleLock::Validation:0xb6875abc @type=:hour>], :interval=>[#<IceCube::Validations::MonthlyInterval::Validation:0xb6875d28 @interval=1>], :base_min=>[#<IceCube::Validations::ScheduleLock::Validation:0xb6875a6c @type=:min>], :base_sec=>[#<IceCube::Validations::ScheduleLock::Validation:0xb6875a1c @type=:sec>]}, @interval=1>]
Then checking Date.today
works...
1.8.7 :025 > schedule.occurs_on? Date.today
=> true
But checking occurs_on? for Date.today + 10.days
still returns false... Why?
1.8.7 :026 > schedule.occurs_on? Date.today + 10.days
=> false
So what am I overlooking / doing wrong? Or what is the point of setting an IceCube::Schedule start_time
and end_time
- they seem to have no effect...?
Does IceCube
not work for single occurrence events with a start and end time?
Another example scenario, a room owner wants room prices raised for a holiday season. So the room owner creates a price schedule that starts on Dec 1 2012 and ends Jan 7 2013. (shouldn't have to recur, but could if the owner wanted).
Then when people are searching rooms, the prices would be adjusted if the requested stay occurs_on?
a holiday price schedule
Do I need to store the start_time
and end_time
outside of the schedule and check it manually or something?
Or is there a better suited gem / tool to assist with this kind of schedule management?
You're misunderstanding how schedules and rules work.
Firstly, it's important to understand start_time
. Every occurrence of the schedule is based on this, and the schedule returns times that match specific intervals from the start time. The intervals are determined by Rules.
Your example doesn't work because "5 days from now" is not a monthly interval from the schedule's start time. 28, 30, or 31 days from the start time would match, depending on the month.
start = Time.utc(2013, 05, 17, 12, 30, 00) # 2013-05-17 12:30:00 UTC
schedule = IceCube::Schedule.new(start)
schedule.add_recurrence_rule IceCube::Rule.monthly
schedule.occurs_on? start + 5.days #=> false
schedule.occurs_on? start + 31.days #=> true
Secondly, end_time
works together with start_time
to set the duration of each occurrence. So if your start time is 09:00, and end time is 17:00 then each occurrence will have a duration of 8 hours.
This creates a distinction between occurs_at?(t1)
and occurring_at?(t1)
: the first one is only true when the given time exactly matches the start of an occurrence; the second one is true for any time in the duration. occurs_on?(d1)
matches for any time in the given date.
arrival = Time.utc(2013, 5, 1, 9, 0, 0)
departure = Time.utc(2013, 5, 1, 17, 0, 0)
schedule = IceCube::Schedule.new(arrival, end_time: departure)
schedule.add_recurrence_rule IceCube::Rule.weekly.day(1, 2, 3, 4, 5) # M-F
schedule.occurs_at? arrival #=> true
schedule.occurs_at? arrival + 1.second #=> false
schedule.occurring_at? arrival + 1.second #=> true
schedule.occurring_at? departure + 1.second #=> false
For what you're doing, you could try one of two approaches:
- A single month-long occurrence
- A daily occurrence that ends after a month
This depends on how you need to display or validate times against the schedule. Here's an example of both:
arrival = Time.utc(2013, 5, 1)
departure = Time.utc(2013, 5, 31)
# single occurrence
schedule = IceCube::Schedule.new(arrival, duration: 31.days)
# daily occurrence
schedule = IceCube::Schedule.new(arrival, duration: 1.day)
schedule.add_recurrence_rule IceCube::Rule.daily.until(departure)
After some more testing I think using IceCube's SingleOccurrenceRule is the proper way to have a single occurrence of an event.
To have a schedule that occurs only on the days between the Schedule start_time
and end_time
I can do something like the following.
Create an IceCube::Schedule
with a start and end_time:
1.8.7 :097 > schedule = IceCube::Schedule.new(Time.now, :end_time => Time.now + 30.days)
=> #<IceCube::Schedule:0xb63caabc @all_recurrence_rules=[], @duration=nil, @end_time=Wed Jan 09 00:03:36 -0600 2013, @all_exception_rules=[], @start_time=Mon Dec 10 00:03:36 -0600 2012>
Put all the days that occur within the schedule into an array.
1.8.7 :098 > days_in_schedule = []
=> []
1.8.7 :099 > schedule.start_time.to_date.upto(schedule.end_time.to_date) { |d| puts d; days_in_schedule << d }
Iterate over the array and create a SingleOccurrenceRule for each day in the schedule. Then test a couple dates. Within 30 days, occurs_on? is true, outside of 30 days, occurs_on? is false. This seems correct, except it still returns false when checking if schedule.occurs_on? Date.today
. WHY?!?!?
1.8.7 :100 > days_in_schedule.each { |d| schedule.rtime Time.parse(d.to_s) }
1.8.7 :109 > schedule.terminating?
=> true
1.8.7 :110 > schedule.occurs_on? Date.today + 5.days
=> true
1.8.7 :111 > schedule.occurs_on? Date.today + 55.days
=> false
1.8.7 :135 > schedule.occurs_on? Date.today
=> false