Is there a built in group of default date ranges in Ruby or in Rails?

Viewed 87

I have a model that needs to check against some obvious common date ranges or a custom date range. Is there a built in way to do this in Ruby or Rails or is there at least a more elegant way to write this?

class Model < ApplicationRecord
  attr_accessor :range
  RANGES = ['Today','This Week','This Month','Yesterday','Last Week','Last Month','Custom']

private

  def set_range
    if range.present?
      if range == 'Today'
        self.start_date = Date.today.beginning_of_day
        self.end_date = Date.today.end_of_day
      elsif range == 'This Week'
        self.start_date = Date.today.beginning_of_week
        self.end_date = Date.today.end_of_week
      elsif range == 'This Month'
        self.start_date = Date.today.beginning_of_month
        self.end_date = Date.today.end_of_month
      elsif range == 'Yesterday'
        self.start_date = 1.day.ago.beginning_of_day
        self.end_date = 1.day.ago.end_of_day
      elsif range == 'Last Week'
        self.start_date = 1.week.ago.beginning_of_week
        self.end_date = 1.week.ago.end_of_week
      elsif range == 'Last Month'
        self.start_date = 1.month.ago.beginning_of_month
        self.end_date = 1.month.ago.end_of_month
      elsif range == 'Custom'
        self.start_date = start_date.beginning_of_day if start_date.present?
        self.end_date = end_date.beginning_of_day if end_date.present?
      end
    end
  end
end
2 Answers

There's nothing built in to my knowledge, but I'd probably do something like the following:

  def set_range
    return unless RANGES.include?(range)

    # if you need dates
    self.start_date, self.end_date = range_dates(range)

    # if you need date/times (renamed attribute)
    dates = range_dates(range)
    self.starts = dates.first&.beginning_of_day
    self.ends   = dates.last&.end_of_day
  end

  def range_dates(range)
    today = Date.today

    case range
      when 'Today'
        [today, today]
      when 'This Week'
        [today.beginning_of_week, today.end_of_week]
      when 'This Month'
        [today.beginning_of_month, today.end_of_month]
      when 'Yesterday'
        [today.yesterday, today.yesterday]
      when 'Last Week'
        [today.last_week.beginning_of_week, today.last_week.end_of_week]
      when 'Last Month'
        [today.last_month.beginning_of_month, today.last_month.end_of_month]
      when 'Custom'
        [start_date, end_date]
    end
  end

For querying you could use the groupdate gem (https://github.com/ankane/groupdate) . Within its codebase you surely do find some time ranges (called periods in that gem if I am not mistaken). However I am not sure if you can pull them out as date ranges; if not, that would be a gread addition to that gem - or better: extract it in another gem. I myself do stumble about that need from time to time.

Then, for easy 'words' -> timespan parsing, there is chronic: https://github.com/mojombo/chronic (which also allows for stuff like "last winter"). However, this seems like a waste of resources, as you would only need very few cases, which can be "precompiled" and do not need to be parsed at runtime .

All in all your approach (and @dinjas improvements) look fine to me, but a gem that gives a quick access (e.g. TimeRanges.last_month) is really kind of missing in the ecosystem, to my knowledge. But implementation is not too difficult, as your snippet shows.

Related