Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single occurrence event with ice_cube gem using start_time and end_time

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?

like image 587
b2tech Avatar asked Jan 15 '23 19:01

b2tech


2 Answers

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:

  1. A single month-long occurrence
  2. 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)
like image 126
Andrew Vit Avatar answered Jan 17 '23 08:01

Andrew Vit


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 
like image 39
b2tech Avatar answered Jan 17 '23 08:01

b2tech