Commit e2bc9b5a82697b804ebd182702cfe341266364ae
Exists in
master
and in
10 other branches
Merge branch 'master' into rails5
Showing
8 changed files
with
79 additions
and
13 deletions
Show diff stats
CHANGELOG.md
README.md
lib/groupdate.rb
... | ... | @@ -4,8 +4,10 @@ require "groupdate/version" |
4 | 4 | require "groupdate/magic" |
5 | 5 | |
6 | 6 | module Groupdate |
7 | - FIELDS = [:second, :minute, :hour, :day, :week, :month, :year, :day_of_week, :hour_of_day, :day_of_month, :month_of_year] | |
8 | - METHODS = FIELDS.map { |v| :"group_by_#{v}" } | |
7 | + PERIODS = [:second, :minute, :hour, :day, :week, :month, :quarter, :year, :day_of_week, :hour_of_day, :day_of_month, :month_of_year] | |
8 | + # backwards compatibility for anyone who happened to use it | |
9 | + FIELDS = PERIODS | |
10 | + METHODS = PERIODS.map { |v| :"group_by_#{v}" } | |
9 | 11 | |
10 | 12 | mattr_accessor :week_start, :day_start, :time_zone |
11 | 13 | self.week_start = :sun | ... | ... |
lib/groupdate/enumerable.rb
1 | 1 | module Enumerable |
2 | - Groupdate::FIELDS.each do |field| | |
3 | - define_method :"group_by_#{field}" do |*args, &block| | |
2 | + Groupdate::PERIODS.each do |period| | |
3 | + define_method :"group_by_#{period}" do |options = {}, &block| | |
4 | 4 | if block |
5 | - Groupdate::Magic.new(field, args[0] || {}).group_by(self, &block) | |
5 | + Groupdate::Magic.new(period, options).group_by(self, &block) | |
6 | 6 | elsif respond_to?(:scoping) |
7 | 7 | scoping { @klass.send(:"group_by_#{field}", *args, &block) } |
8 | 8 | else |
... | ... | @@ -13,7 +13,7 @@ module Enumerable |
13 | 13 | |
14 | 14 | def group_by_period(period, options = {}, &block) |
15 | 15 | # to_sym is unsafe on user input, so convert to strings |
16 | - permitted_periods = ((options[:permit] || Groupdate::FIELDS).map(&:to_sym) & Groupdate::FIELDS).map(&:to_s) | |
16 | + permitted_periods = ((options[:permit] || Groupdate::PERIODS).map(&:to_sym) & Groupdate::PERIODS).map(&:to_s) | |
17 | 17 | if permitted_periods.include?(period.to_s) |
18 | 18 | send("group_by_#{period}", options, &block) |
19 | 19 | else | ... | ... |
lib/groupdate/magic.rb
... | ... | @@ -45,6 +45,8 @@ module Groupdate |
45 | 45 | ["MONTH(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL #{day_start} HOUR), '+00:00', ?))", time_zone] |
46 | 46 | when :week |
47 | 47 | ["CONVERT_TZ(DATE_FORMAT(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL ((#{7 - week_start} + WEEKDAY(CONVERT_TZ(#{column}, '+00:00', ?) - INTERVAL #{day_start} HOUR)) % 7) DAY) - INTERVAL #{day_start} HOUR, '+00:00', ?), '%Y-%m-%d 00:00:00') + INTERVAL #{day_start} HOUR, ?, '+00:00')", time_zone, time_zone, time_zone] |
48 | + when :quarter | |
49 | + ["DATE_ADD(CONVERT_TZ(DATE_FORMAT(DATE(CONCAT(EXTRACT(YEAR FROM CONVERT_TZ(DATE_SUB(#{column}, INTERVAL #{day_start} HOUR), '+00:00', ?)), '-', LPAD(1 + 3 * (QUARTER(CONVERT_TZ(DATE_SUB(#{column}, INTERVAL #{day_start} HOUR), '+00:00', ?)) - 1), 2, '00'), '-01')), '%Y-%m-%d %H:%i:%S'), ?, '+00:00'), INTERVAL #{day_start} HOUR)", time_zone, time_zone, time_zone] | |
48 | 50 | else |
49 | 51 | format = |
50 | 52 | case field |
... | ... | @@ -201,7 +203,11 @@ module Groupdate |
201 | 203 | if time_range.first |
202 | 204 | series = [round_time(time_range.first)] |
203 | 205 | |
204 | - step = 1.send(field) | |
206 | + if field == :quarter | |
207 | + step = 3.months | |
208 | + else | |
209 | + step = 1.send(field) | |
210 | + end | |
205 | 211 | |
206 | 212 | while (next_step = round_time(series.last + step)) && time_range.cover?(next_step) |
207 | 213 | series << next_step |
... | ... | @@ -278,6 +284,8 @@ module Groupdate |
278 | 284 | (time - ((7 - week_start + weekday) % 7).days).midnight |
279 | 285 | when :month |
280 | 286 | time.beginning_of_month |
287 | + when :quarter | |
288 | + time.beginning_of_quarter | |
281 | 289 | when :year |
282 | 290 | time.beginning_of_year |
283 | 291 | when :hour_of_day | ... | ... |
lib/groupdate/scopes.rb
1 | 1 | module Groupdate |
2 | 2 | module Scopes |
3 | - Groupdate::FIELDS.each do |field| | |
4 | - define_method :"group_by_#{field}" do |*args| | |
3 | + Groupdate::PERIODS.each do |period| | |
4 | + define_method :"group_by_#{period}" do |field, *args| | |
5 | 5 | args = args.dup |
6 | 6 | options = args[-1].is_a?(Hash) ? args.pop : {} |
7 | - options[:time_zone] ||= args[1] unless args[1].nil? | |
8 | - options[:range] ||= args[2] unless args[2].nil? | |
7 | + options[:time_zone] ||= args[0] unless args[0].nil? | |
8 | + options[:range] ||= args[1] unless args[1].nil? | |
9 | 9 | |
10 | - Groupdate::Magic.new(field, options).relation(args[0], self) | |
10 | + Groupdate::Magic.new(period, options).relation(field, self) | |
11 | 11 | end |
12 | 12 | end |
13 | 13 | |
14 | 14 | def group_by_period(period, field, options = {}) |
15 | 15 | # to_sym is unsafe on user input, so convert to strings |
16 | - permitted_periods = ((options[:permit] || Groupdate::FIELDS).map(&:to_sym) & Groupdate::FIELDS).map(&:to_s) | |
16 | + permitted_periods = ((options[:permit] || Groupdate::PERIODS).map(&:to_sym) & Groupdate::PERIODS).map(&:to_s) | |
17 | 17 | if permitted_periods.include?(period.to_s) |
18 | 18 | send("group_by_#{period}", field, options) |
19 | 19 | else | ... | ... |
test/postgresql_test.rb
test/test_helper.rb
... | ... | @@ -230,6 +230,42 @@ module TestGroupdate |
230 | 230 | assert_result_time :month, "2013-03-01 02:00:00 PST", "2013-03-01 10:00:00", true, day_start: 2 |
231 | 231 | end |
232 | 232 | |
233 | + # quarter | |
234 | + | |
235 | + def test_quarter_end_of_quarter | |
236 | + assert_result_time :quarter, "2013-04-01 00:00:00 UTC", "2013-06-30 23:59:59" | |
237 | + end | |
238 | + | |
239 | + def test_quarter_start_of_quarter | |
240 | + assert_result_time :quarter, "2013-04-01 00:00:00 UTC", "2013-04-01 00:00:00" | |
241 | + end | |
242 | + | |
243 | + def test_quarter_end_of_quarter_with_time_zone | |
244 | + assert_result_time :quarter, "2013-04-01 00:00:00 PDT", "2013-07-01 06:59:59", true | |
245 | + end | |
246 | + | |
247 | + def test_quarter_start_of_quarter_with_time_zone | |
248 | + assert_result_time :quarter, "2013-04-01 00:00:00 PDT", "2013-04-01 07:00:00", true | |
249 | + end | |
250 | + | |
251 | + # quarter starts at 2am | |
252 | + | |
253 | + def test_quarter_end_of_quarter_day_start_2am | |
254 | + assert_result_time :quarter, "2013-04-01 02:00:00 UTC", "2013-07-01 01:59:59", false, day_start: 2 | |
255 | + end | |
256 | + | |
257 | + def test_quarter_start_of_quarter_day_start_2am | |
258 | + assert_result_time :quarter, "2013-04-01 02:00:00 UTC", "2013-04-01 02:00:00", false, day_start: 2 | |
259 | + end | |
260 | + | |
261 | + def test_quarter_end_of_quarter_with_time_zone_day_start_2am | |
262 | + assert_result_time :quarter, "2013-01-01 02:00:00 PST", "2013-04-01 08:59:59", true, day_start: 2 | |
263 | + end | |
264 | + | |
265 | + def test_quarter_start_of_quarter_with_time_zone_day_start_2am | |
266 | + assert_result_time :quarter, "2013-01-01 02:00:00 PST", "2013-01-01 10:00:00", true, day_start: 2 | |
267 | + end | |
268 | + | |
233 | 269 | # year |
234 | 270 | |
235 | 271 | def test_year_end_of_year |
... | ... | @@ -468,6 +504,14 @@ module TestGroupdate |
468 | 504 | assert_zeros :month, "2013-04-16 20:00:00 PDT", ["2013-03-01 00:00:00 PST", "2013-04-01 00:00:00 PDT", "2013-05-01 00:00:00 PDT"], "2013-03-01 00:00:00 PST", "2013-05-31 23:59:59 PDT", true |
469 | 505 | end |
470 | 506 | |
507 | + def test_zeros_quarter | |
508 | + assert_zeros :quarter, "2013-04-16 20:00:00 UTC", ["2013-01-01 00:00:00 UTC", "2013-04-01 00:00:00 UTC", "2013-07-01 00:00:00 UTC"], "2013-01-01 00:00:00 UTC", "2013-09-30 23:59:59 UTC" | |
509 | + end | |
510 | + | |
511 | + def test_zeros_quarter_time_zone | |
512 | + assert_zeros :quarter, "2013-04-16 20:00:00 PDT", ["2013-01-01 00:00:00 PST", "2013-04-01 00:00:00 PDT", "2013-07-01 00:00:00 PDT"], "2013-01-01 00:00:00 PST", "2013-09-30 23:59:59 PDT", true | |
513 | + end | |
514 | + | |
471 | 515 | def test_zeros_year |
472 | 516 | assert_zeros :year, "2013-04-16 20:00:00 UTC", ["2012-01-01 00:00:00 UTC", "2013-01-01 00:00:00 UTC", "2014-01-01 00:00:00 UTC"], "2012-01-01 00:00:00 UTC", "2014-12-31 23:59:59 UTC" |
473 | 517 | end |
... | ... | @@ -723,6 +767,11 @@ module TestGroupdate |
723 | 767 | assert_format :month, "March 2014", "%B %Y" |
724 | 768 | end |
725 | 769 | |
770 | + def test_format_quarter | |
771 | + create_user "2014-03-05 00:00:00 UTC" | |
772 | + assert_format :quarter, "January 1, 2014", "%B %-e, %Y" | |
773 | + end | |
774 | + | |
726 | 775 | def test_format_year |
727 | 776 | create_user "2014-03-01 00:00:00 UTC" |
728 | 777 | assert_format :year, "2014", "%Y" | ... | ... |