Groupdate
The simplest way to group by:
- day
- week
- hour of the day
- and more (complete list below)
š Time zones - including daylight saving time - supported!! the best part
š° Get the entire series - the other best part
Supports PostgreSQL, MySQL, MariaDB, SQLite, and Redshift, plus arrays and hashes
š Goes hand in hand with Chartkick
Installation
Add this line to your applicationās Gemfile:
gem "groupdate"For MySQL and MariaDB, also follow these instructions.
Getting Started
User.group_by_day(:created_at).count
# {
# Wed, 01 Jan 2025 => 50,
# Thu, 02 Jan 2025 => 100,
# Fri, 03 Jan 2025 => 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
and
- 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)"or
User.group_by_week(:created_at, time_zone: "Pacific Time (US & Canada)").count
# {
# Sun, 05 Jan 2025 => 70,
# Sun, 12 Jan 2025 => 54,
# Sun, 19 Jan 2025 => 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 = :mondayor
User.group_by_week(:created_at, week_start: :monday).countDay Start
You can change the hour days start with:
Groupdate.day_start = 2 # 2 am - 2 amor
User.group_by_day(:created_at, day_start: 2).countTime Range
To get a specific time range, use:
User.group_by_day(:created_at, range: 2.weeks.ago.midnight..Time.now).countTo 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).countTo get the most recent time periods, use:
User.group_by_week(:created_at, last: 8).count # last 8 weeksTo exclude the current period, use:
User.group_by_week(:created_at, last: 8, current: false).countOrder
You can order in descending order with:
User.group_by_day(:created_at, reverse: true).countKeys
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 2025" => 10
# "Feb 2025" => 12
# }or
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.
Series
The entire series is returned by default. To exclude points without data, use:
User.group_by_day(:created_at, series: false).countOr change the default value with:
User.group_by_day(:created_at, default_value: "missing").countDynamic Grouping
User.group_by_period(:day, :created_at).countLimit groupings with the permit option.
User.group_by_period(params[:period], :created_at, permit: ["day", "week"]).countRaises 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 minutesDate Columns
If grouping on date columns which donāt need time zone conversion, use:
User.group_by_week(:created_on, time_zone: false).countDefault 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:
User.unscope(:order).group_by_day(:count).countArrays 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 }Count
users.group_by_day { |u| u.created_at }.to_h { |k, v| [k, v.count] }Additional Instructions
For MySQL and MariaDB
Time zone support must be installed on the server.
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysqlYou can confirm it worked with:
SELECT CONVERT_TZ(NOW(), '+00:00', 'Pacific/Honolulu');It should return the time instead of NULL.
History
View the changelog
Contributing
Everyone is encouraged to help improve this project. Here are a few ways you can help:
- Report bugs
- Fix bugs and submit pull requests
- Write, clarify, or fix documentation
- Suggest or add new features
To get started with development and testing, check out the Contributing Guide.