Code Quality Rank: L3
Programming language: Ruby
License: MIT License
Latest version: v6.1.0

groupdate alternatives and similar gems

Based on the "Date and Time Processing" category.
Alternatively, view groupdate alternatives based on common mentions on social networks and blogs.

Do you think we are missing an alternative of groupdate or a related project?

Add another 'Date and Time Processing' Gem



The simplest way to group by:

  • day
  • week
  • hour of the day
  • and more (complete list below)

:tada: Time zones - including daylight saving time - supported!! the best part

:cake: Get the entire series - the other best part

Supports PostgreSQL, MySQL, and Redshift, plus arrays and hashes (and limited support for SQLite)

:cupid: Goes hand in hand with Chartkick

Build Status


Add this line to your application’s Gemfile:

gem "groupdate"

For MySQL and SQLite, also follow these instructions.

Getting Started

# {
#   Sat, 24 May 2020 => 50,
#   Sun, 25 May 2020 => 100,
#   Mon, 26 May 2020 => 34
# }

Results are returned in ascending order by default, so no need to sort.

You can group by:

  • second
  • minute
  • hour
  • day
  • week
  • month
  • quarter
  • year


  • minute_of_hour
  • hour_of_day
  • day_of_week (Sunday = 0, Monday = 1, etc)
  • day_of_month
  • day_of_year
  • month_of_year

Use it anywhere you can use group. Works with count, sum, minimum, maximum, and average. For median and percentile, check out ActiveMedian.

Time Zones

The default time zone is Time.zone. Change this with:

Groupdate.time_zone = "Pacific Time (US & Canada)"


User.group_by_week(:created_at, time_zone: "Pacific Time (US & Canada)").count
# {
#   Sun, 08 Mar 2020 => 70,
#   Sun, 15 Mar 2020 => 54,
#   Sun, 22 Mar 2020 => 80
# }

Time zone objects also work. To see a list of available time zones in Rails, run rake time:zones:all.

Week Start

Weeks start on Sunday by default. Change this with:

Groupdate.week_start = :monday


User.group_by_week(:created_at, week_start: :monday).count

Day Start

You can change the hour days start with:

Groupdate.day_start = 2 # 2 am - 2 am


User.group_by_day(:created_at, day_start: 2).count

Time Range

To get a specific time range, use:

User.group_by_day(:created_at, range: 2.weeks.ago.midnight..Time.now).count

To expand the range to the start and end of the time period, use:

User.group_by_day(:created_at, range: 2.weeks.ago..Time.now, expand_range: true).count

To get the most recent time periods, use:

User.group_by_week(:created_at, last: 8).count # last 8 weeks

To exclude the current period, use:

User.group_by_week(:created_at, last: 8, current: false).count


You can order in descending order with:

User.group_by_day(:created_at, reverse: true).count


Keys are returned as date or time objects for the start of the period.

To get keys in a different format, use:

User.group_by_month(:created_at, format: "%b %Y").count
# {
#   "Jan 2020" => 10
#   "Feb 2020" => 12
# }


User.group_by_hour_of_day(:created_at, format: "%-l %P").count
# {
#    "12 am" => 15,
#    "1 am"  => 11
#    ...
# }

Takes a String, which is passed to strftime, or a Symbol, which is looked up by I18n.localize in i18n scope 'time.formats', or a Proc. You can pass a locale with the locale option.


The entire series is returned by default. To exclude points without data, use:

User.group_by_day(:created_at, series: false).count

Or change the default value with:

User.group_by_day(:created_at, default_value: "missing").count

Dynamic Grouping

User.group_by_period(:day, :created_at).count

Limit groupings with the permit option.

User.group_by_period(params[:period], :created_at, permit: ["day", "week"]).count

Raises an ArgumentError for unpermitted periods.

Custom Duration

To group by a specific number of minutes or seconds, use:

User.group_by_minute(:created_at, n: 10).count # 10 minutes

Date Columns

If grouping on date columns which don’t need time zone conversion, use:

User.group_by_week(:created_on, time_zone: false).count

Default Scopes

If you use Postgres and have a default scope that uses order, you may get a column must appear in the GROUP BY clause error (just like with Active Record’s group method). Remove the order scope with:


Arrays and Hashes

users.group_by_day { |u| u.created_at } # or group_by_day(&:created_at)

Supports the same options as above

users.group_by_day(time_zone: time_zone) { |u| u.created_at }

Get the entire series with:

users.group_by_day(series: true) { |u| u.created_at }


users.group_by_day { |u| u.created_at }.to_h { |k, v| [k, v.count] }

Additional Instructions


Time zone support must be installed on the server.

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql

You can confirm it worked with:

SELECT CONVERT_TZ(NOW(), '+00:00', 'Pacific/Honolulu');

It should return the time instead of NULL.

For SQLite

Groupdate has limited support for SQLite.

  • No time zone support
  • No day_start option
  • No group_by_quarter method

If your application’s time zone is set to something other than Etc/UTC (the default), create an initializer with:

Groupdate.time_zone = false



Groupdate 6.0 protects against unsafe input by default. For non-attribute arguments, use:


Also, the dates option has been removed.


View the changelog


Everyone is encouraged to help improve this project. Here are a few ways you can help:

To get started with development and testing, check out the [Contributing Guide](CONTRIBUTING.md).