Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Make Your Models With Start and/or End Dates Bow to Your Will

README.md

Chronological: The 'Start' of Your Fun Will Never 'End'

Build Status

Chronological is your one-stop solution for handling time ranges in your classes.

Have an item that is only available between two dates/times? In these situations, there is quite a bit of logic which is common among use cases and that's what Chronological provides.

Clock Tower Flier

Supported Rubies

  • MRI Ruby 1.9.2
  • MRI Ruby 1.9.3
  • JRuby (in 1.9 compat mode)

Installation

First:

gem install chronological

Then in your script:

require 'chronological'

Finally in your class:

include Chronological

or from IRB

irb -r 'chronological'

Basic Usage

The easiest way to use Chronological is to just let it know which strategy you would like to use for the time range for that particular object.

class MyTimeRangeClass
  include Chronological

  timeframe  :type => :absolute
end

This will look for a started_at and an ended_at method on your object and will base all of its dynamic methods on those.

Now you can use any of the methods that Chronological adds to your class:


Range Status Predicates

  • started? - Whether it is currently on or after the start date
  • ended? - Whether it is currently on or after the end date
  • not_yet_ended? - Whether it is currently before the end date
  • in_progress? (aliased to active?) - Whether it is between the start and end dates
  • inactive? - The inverse of in_progress?/active?

Scheduling Validity Predicates

  • scheduled? - Whether all of the pieces needed to determine a timeframe have been set
  • partially_scheduled? - Whether any of the pieces needed to determine a timeframe have been set

Range Type Predicates

  • same_day? - Whether the starting time and the ending time are on the same day
  • one_day? - Whether the starting time and the ending time are less than 24 hours apart
  • multi_day? - Whether the starting time and the ending time are on different days

Other Methods

  • duration - A Hash containing the days, hours, minutes and seconds

Scopes

Each of these methods also has a corresponding ActiveRelation method that will represents all of the items that meet the requirements of that method.

  • started
  • ended
  • not_yet_ended
  • in_progress (also aliased to active?)
  • scheduled
  • partially_scheduled
  • one_day
  • multi_day

Example

If this is true:

range_instance = InstanceWithTimeRange.create

range_instance.started? #=> true

Then this would also be the case:

expect { InstanceWithTimeRange.started }.to include range_instance #=> true

Ordering

  • by_duration(:asc) - Sort the results by the length of the duration
  • by_date(:asc) - Intelligently sort based on the start and end date

Advanced Usage

Note 1: All durations and offsets are represented in seconds. Note 2: All 'Defaults' are in the same order as the 'Options' above them.

Strategies

Chronological is extremely flexible and can handle multiple strategies for calculating your time range information.

If you choose to use our default field names, you can simply pass the strategy in as a symbol and it will Just Work(tm).

class MyTimeRangeClass
  timeframe :type => :duration_until_end
end

Alternatively, you can pass in the specific set of options per strategy as a hash and set the values to the field names you want Chronological to use.

class MyTimeRangeClass
  timeframe :ending_time => :available_until,
            :duration    => :length_of_availability
end

Absolute

Options: starting_time and ending_time

Defaults: started_at and ended_at

Example: The concert starts at 5:30pm and ends at 11:30pm


Relative

Options: base_of_offset, starting_offset and ending_offset

Defaults: base_of_range_offset, start_of_range_offset and end_of_range_offset

Example: You can buy the ticket anytime between 1-2 weeks before the event starts


Dual Relative

Options: base_of_starting_offset, starting_offset, base_of_ending_offset and ending_offset

Defaults: base_of_range_starting_offset, start_of_range_offset, base_of_range_ending_offset and end_of_range_offset

Example: The coupon is active from 2 hours after the first sale until 3 hours before the store closes


Duration From Start

Options: starting_time, duration

Defaults: started_at and duration_in_seconds

Example: The donut shop opens at 7am but only for 30 minutes


Duration Until End

Options: ending_time, duration

Defaults: ended_at and duration_in_seconds

Example: Your 'roadie' pass will only get you backstage for the 30 minutes before the band goes on stage at 9pm


Duration From Relative Start

Options: base_of_starting_offset, starting_offset and duration

Defaults: base_of_range_starting_offset, start_of_range_offset and duration_in_seconds

Example: The party will start 30 minutes after the concert ends and last for 4 hours


Duration Until A Relative End

Options: base_of_ending_offset, ending_offset and duration

Defaults: base_of_range_ending_offset, end_of_range_offset and duration_in_seconds

Example: The secret vault will be open for 3 hours and will relock 15 minutes prior to the office opening

Determining Absolute Dates

Other than the 'Absolute' strategy above, any of those combinations will calculate the absolute dates of the time range and make them available via these accessors:

  • started_at
  • ended_at
  • started_on
  • ended_on

If you want to override the fields that Chronological creates to access those values, simply pass in the option with the name of the field you would like.

Example

class MyTimeRangeClass
  timeframe :base_of_offset  => :event_start_time,
            :starting_offset => :starting_availability_offset
            :ending_offset   => :ending_availability_offset,
            :starting_time   => :custom_start_field
            :ending_time     => :custom_end_field
end

Note: The :base_of_offset option can be anything that has accessor methods for the field.

Advanced Method Usage


Range Status As Of A Given Date

All range status methods can take an :as_of option which will replace the default behavior which is Time.now.utc. Using this option you can more easily see if an instance (or instances) would be started, ended, etc as of a given date.

All range status methods can also take a :base_of option this is only meaningful for strategies which utilize an offset for either the start time or the end time which will calculate the absolute time based on that time rather than any time stored within the instance.

Affected methods:

  • started?
  • ended?
  • not_yet_ended?
  • in_progress? (or active?)
  • inactive?

Affected scopes:

  • started
  • ended
  • not_yet_ended
  • in_progress (or active)
  • inactive?
range_instance = MyTimeRangeClass.create

range_instance.ended?                               #=> false
range_instance.ended? :as_of => 42.years.from_now   #=> true

MyTimeRangeClass.ended                              #=> []
MyTimeRangeClass.ended :as_of => 42.years.from_now  #=> [range_instance]

Duration Components

When calling duration on a Chronological, by default it will return the days, hours, minutes and seconds in a Hash. If you need a specific combination of these pieces, you can pass them in an :in options like so:

# range_instance's total duration is 5 days, 1 hour, 44 minutes and 23 seconds

range_instance.duration :in => [:days, :hours, :seconds]
#=> { :days => 5, :hours => 1, :seconds => 2663 }

range_instance.duration :in => [:days, :hours, :minutes]
#=> { :days => 5, :hours => 1, :minutes => 44 }

range_instance.duration :in => [:hours, :minutes, :seconds]
#=> { :hours => 121, :minutes => 44, :seconds => 23 }

Affected methods:

Is It Scheduled?

Even though Chronological does not handle anything having to do with time zones, it is valid however, to assume there will be use cases where, without a time zone, the model should not be considered scheduled.

If you wish to account for this, pass in the :time_zone option to timeframe and give it the field name that contains the time zone you wish to use. For example:

class MyTimeRangeClass
  timeframe  :starting_time => :started_at,
             :ending_time   => :ended_at,
             :time_zone     => :time_zone
end

range_instance            = MyTimeRangeClass.new
range_instance.started_at = 5.minutes.from_now
range_instance.ended_at   = 30.minutes.from_now

range_instance.scheduled? # => false

range_instance.time_zone  = 'Alaska'

range_instance.scheduled? # => true

Issues

If you have problems, please create a Github issue.

Credits

chirrpy

greenwich is maintained by Chrrpy, LLC

The names and logos for Chirrpy are trademarks of Chrrpy, LLC

Contributors

License

chronological is Copyright © 2012 Chirrpy. It is free software, and may be redistributed under the terms specified in the LICENSE file.

Something went wrong with that request. Please try again.