lib/groupdate/series_builder.rb in groupdate-4.0.2 vs lib/groupdate/series_builder.rb in groupdate-4.1.0

- old
+ new

@@ -1,15 +1,18 @@ module Groupdate class SeriesBuilder attr_reader :period, :time_zone, :day_start, :week_start, :options + CHECK_PERIODS = [:day, :week, :month, :quarter, :year] + def initialize(period:, time_zone:, day_start:, week_start:, **options) @period = period @time_zone = time_zone @week_start = week_start @day_start = day_start @options = options + @round_time = {} end def generate(data, default_value:, series_default: true, multiple_groups: false, group_index: nil) series = generate_series(data, multiple_groups, group_index) series = handle_multiple(data, series, multiple_groups, group_index) @@ -17,26 +20,37 @@ unless entire_series?(series_default) series = series.select { |k| data[k] } end value = 0 - Hash[series.map do |k| - value = data[k] || (@options[:carry_forward] && value) || default_value + result = Hash[series.map do |k| + value = data.delete(k) || (@options[:carry_forward] && value) || default_value key = if multiple_groups k[0...group_index] + [key_format.call(k[group_index])] + k[(group_index + 1)..-1] else key_format.call(k) end [key, value] end] + + # only check for database + # only checks remaining keys to avoid expensive calls to round_time + if series_default && CHECK_PERIODS.include?(period) + check_consistent_time_zone_info(data, multiple_groups, group_index) + end + + result end def round_time(time) - time = time.to_time.in_time_zone(time_zone) - day_start.seconds + time = time.to_time.in_time_zone(time_zone) + # only if day_start != 0 for performance + time -= day_start.seconds if day_start != 0 + time = case period when :second time.change(usec: 0) when :minute @@ -67,11 +81,14 @@ time.month else raise Groupdate::Error, "Invalid period" end - time.is_a?(Time) ? time + day_start.seconds : time + # only if day_start != 0 for performance + time += day_start.seconds if day_start != 0 && time.is_a?(Time) + + time end def time_range @time_range ||= begin time_range = options[:range] @@ -155,11 +172,15 @@ else step = 1.send(period) end last_step = series.last - while (next_step = round_time(last_step + step)) && time_range.cover?(next_step) + loop do + next_step = last_step + step + next_step = round_time(next_step) if next_step.hour != 0 # add condition to speed up + break unless time_range.cover?(next_step) + if next_step == last_step last_step += step next end series << next_step @@ -216,9 +237,24 @@ end elsif reverse series.to_a.reverse else series + end + end + + def check_consistent_time_zone_info(data, multiple_groups, group_index) + keys = data.keys + if multiple_groups + keys.map! { |k| k[group_index] } + keys.uniq! + end + + keys.each do |key| + if key != round_time(key) + # only need to show what database returned since it will cast in Ruby time zone + raise Groupdate::Error, "Database and Ruby have inconsistent time zone info. Database returned #{key}" + end end end def entire_series?(series_default) options.key?(:series) ? options[:series] : series_default