lib/groupdate/series_builder.rb in groupdate-5.2.4 vs lib/groupdate/series_builder.rb in groupdate-6.0.0
- old
+ new
@@ -1,11 +1,9 @@
module Groupdate
class SeriesBuilder
attr_reader :period, :time_zone, :day_start, :week_start, :n_seconds, :options
- CHECK_PERIODS = [:day, :week, :month, :quarter, :year]
-
def initialize(period:, time_zone:, day_start:, week_start:, n_seconds:, **options)
@period = period
@time_zone = time_zone
@week_start = week_start
@day_start = day_start
@@ -21,55 +19,26 @@
verified_data = {}
series.each do |k|
verified_data[k] = data.delete(k)
end
- # this is a fun one
- # PostgreSQL and Ruby both return the 2nd hour when converting/parsing a backward DST change
- # Other databases and Active Support return the 1st hour (as expected)
- # Active Support good: ActiveSupport::TimeZone["America/Los_Angeles"].parse("2013-11-03 01:00:00")
- # MySQL good: SELECT CONVERT_TZ('2013-11-03 01:00:00', 'America/Los_Angeles', 'Etc/UTC');
- # Ruby not good: Time.parse("2013-11-03 01:00:00")
- # PostgreSQL not good: SELECT '2013-11-03 01:00:00'::timestamp AT TIME ZONE 'America/Los_Angeles';
- # we need to account for this here
- if series_default && CHECK_PERIODS.include?(period)
- data.each do |k, v|
- key = multiple_groups ? k[group_index] : k
- # TODO only do this for PostgreSQL
- # this may mask some inconsistent time zone errors
- # but not sure there's a better approach
- if key.hour == (key - 1.hour).hour && series.include?(key - 1.hour)
- key -= 1.hour
- if multiple_groups
- k[group_index] = key
- else
- k = key
- end
- verified_data[k] = v
- elsif 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
-
unless entire_series?(series_default)
series = series.select { |k| verified_data[k] }
end
value = 0
- result = Hash[series.map do |k|
+ result = series.to_h do |k|
value = verified_data[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]
+ end
result
end
def round_time(time)
@@ -79,11 +48,11 @@
time = time.to_time.in_time_zone(time_zone)
if day_start != 0
# apply day_start to a time object that's not affected by DST
- time = change_zone.call(time, utc)
+ time = time.change(zone: utc)
time -= day_start.seconds
end
time =
case period
@@ -119,41 +88,26 @@
raise Groupdate::Error, "Invalid period"
end
if day_start != 0 && time.is_a?(Time)
time += day_start.seconds
- time = change_zone.call(time, time_zone)
+ time = time.change(zone: time_zone)
end
time
end
- def change_zone
- @change_zone ||= begin
- if ActiveSupport::VERSION::STRING >= "5.2"
- ->(time, zone) { time.change(zone: zone) }
- else
- # TODO make more efficient
- ->(time, zone) { zone.parse(time.strftime("%Y-%m-%d %H:%M:%S")) }
- end
- end
- end
-
def time_range
@time_range ||= begin
time_range = options[:range]
if time_range.is_a?(Range)
# check types
[time_range.begin, time_range.end].each do |v|
case v
when nil, Date, Time
# good
- when String
- # TODO raise error in Groupdate 6
- warn "[groupdate] Range bounds should be Date or Time, not #{v.class.name}. This will raise an error in Groupdate 6"
- break
else
raise ArgumentError, "Range bounds should be Date or Time, not #{v.class.name}"
end
end
@@ -287,11 +241,10 @@
end
def key_format
@key_format ||= begin
locale = options[:locale] || I18n.locale
- use_dates = options.key?(:dates) ? options[:dates] : Groupdate.dates
if options[:format]
if options[:format].respond_to?(:call)
options[:format]
else
@@ -310,10 +263,10 @@
key = Date.new(2014, key, 1).to_time
end
I18n.localize(key, format: options[:format], locale: locale)
end
end
- elsif [:day, :week, :month, :quarter, :year].include?(period) && use_dates
+ elsif [:day, :week, :month, :quarter, :year].include?(period)
lambda { |k| k.to_date }
else
lambda { |k| k }
end
end