Skip to content
Make Your Models With Start and/or End Dates Bow to Your Will
Pull request Compare This branch is 39 commits behind thekompanee:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.

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)



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

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


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


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


  • 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.


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

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


Options: starting_time and ending_time

Defaults: started_at and ended_at

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


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.


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

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 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

range_instance            =
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


If you have problems, please create a Github issue.



greenwich is maintained by Chrrpy, LLC

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



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.