The project is in a healthy, maintained state
ActiveDateRange provides a range of dates with a powerful API to manipulate and use date ranges in your software.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

Active Date Range

ActiveDateRange provides a range of dates with a powerful API to manipulate and use date ranges in your software. Date ranges are commonly used in reporting tools, but can be of use in many situation where you need for example filtering or reporting.

Installation

Add this line to your application's Gemfile:

gem 'active_date_range'

And then execute:

$ bundle install

Or install it yourself as:

$ gem install active_date_range

Usage

Initialize a new date range

Initialize a new range:

ActiveDateRange::DateRange.new(Date.new(2021, 1, 1), Date.new(2021, 12, 31))
ActiveDateRange::DateRange.new(Date.new(2021, 1, 1)..Date.new(2021, 12, 31))

You can also use shorthands to initialize a range relative to today. Shorthands are available for this, prev and next for the ranges month, quarter, year and week:

ActiveDateRange::DateRange.this_month
ActiveDateRange::DateRange.this_year
ActiveDateRange::DateRange.this_quarter
ActiveDateRange::DateRange.this_week
ActiveDateRange::DateRange.prev_month
ActiveDateRange::DateRange.prev_year
ActiveDateRange::DateRange.prev_quarter
ActiveDateRange::DateRange.prev_week
ActiveDateRange::DateRange.next_month
ActiveDateRange::DateRange.next_year
ActiveDateRange::DateRange.next_quarter
ActiveDateRange::DateRange.next_week

The third option is to use parse:

ActiveDateRange::DateRange.parse('202101..202112')
ActiveDateRange::DateRange.parse('20210101..20210115')

Parse accepts three formats: YYYYMM..YYYYMM, YYYYMMDD..YYYYMMDD and any short hand like this_month.

The date range instance

A DateRange object is an extension of a regular Range object. You can use all methods available on Range:

date_range = ActiveDateRange::DateRange.parse('202101..202112')
date_range.begin                            # => Date.new(2021, 1, 1)
date_range.end                              # => Date.new(2021, 12, 31)
date_range.cover?(Date.new(2021, 2, 1))     # => true

DateRange adds extra methods to work with date ranges:

date_range.days                             # => 365
date_range.months                           # => 12
date_range.quarters                         # => 4
date_range.years                            # => 1
date_range.one_month?                       # => false
date_range.one_week?                        # => false
date_range.one_year?                        # => true
date_range.full_year?                       # => true
date_range.same_year?                       # => true
date_range.before?(Date.new(2022, 1, 1))    # => true
date_range.after?(ActiveDateRange::DateRange.parse('202001..202012')) # => true
date_range.granularity                      # => :year
date_range.to_param                         # => "202101..202112"
date_range.to_param(relative: true)         # => "this_year"

You can also do calculations with the ranges:

date_range.previous                             # => DateRange.parse('202001..202012')
date_range.previous(2)                          # => DateRange.parse('201901..202012')
date_range.next                                 # => DateRange.parse('202201..202212')
date_range + DateRange.parse('202201..202202')  # => DateRange.parse('202101..202202')
date_range.in_groups_of(:month)                 # => [DateRange.parse('202101..202101'), ..., DateRange.parse('202112..202112')]
date_range.intersection(DateRange.parse('202101..202102')) # => DateRange.parse('202101..202102')

Support for boundless ranges is also available:

date_range = DateRange.parse('202101..')
date_range.boundless? # => true
date_range.in_groups_of(:month) # => Enumerator::Lazy
Model.where(date: date_range) # => SQL "WHERE date >= 2021-01-01"

And lastly you can call .humanize to get a localizable human representation of the range for in the user interface:

date_range.humanize                      # => '2021'
date_range.humanize(format: :explicit)   # => 'January 1st, 2021 - December 31st 2021'

See active_date_range/locale/en.yml for all the I18n keys you need to translate for your application.

ActiveModel type

Date ranges are also available as an ActiveModel type. So you can use a date range attribute and the value will automatically be converted:

class Report
  include ActiveModel::Attributes

  attribute :period, :date_range
end

Usage example

Use the shorthands to link to a specific period:

<%= link_to "Show report for #{DateRange.this_year.humanize}", report_url(period: DateRange.this_year.to_param(relative: true)) %>

Because we use to_params(relative: true), the user gets a bookmarkable URL which always points to the current year. If you need the URL to be bookmarkable and always point to the same period, remove relative: true.

In your controller, use the parameter in your queries:

def report
  @period = DateRange.parse(params[:period])
  @data = SomeModel.where(date: @period)
end

In the report view, use the period object to render previous and next links:

<%= link_to "Next period", report_url(period: @period.next) %>
<%= link_to "Previous period", report_url(period: @period.previous) %>

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/moneybird/active-date-range.