使用时区时过滤日期时间不起作用

Filtering datetime not working when using timezone

在我的设置中,我想通过 ActivityCityDateStarting TimeEnd Time 过滤 Schedule 使用 select 菜单。

我已经让它与 Ransack 一起工作:

# db
create_table "schedules", force: :cascade do |t|
  t.integer  "city_id"
  t.integer  "activity_id"
  t.datetime "starts_at"
  t.datetime "ends_at"
  ...
end

# Schedule model
scope :six_hours_from_now, -> { where('starts_at >= ?', Time.zone.now + 6.hours) }

ransacker :start_date, type: :date do
  Arel.sql('starts_at::date')   # filter only the date from starts_at attribute
end

ransacker :start_time, type: :time do
  Arel.sql('starts_at::time')   # filter only the time from starts_at attribute
end

ransacker :end_time, type: :time do
  Arel.sql('ends_at::time')     # filter only the time from ends_at attribute
end

# Schedule controller
def index
  @q          = Schedule.ransack(params[:q])
  @q.sorts    = ['starts_at asc', 'ends_at asc'] if @q.sorts.empty?
  @schedules  = @q.result.six_hours_from_now
end

# view
<%= search_form_for @q do |f| %>
  <%= f.collection_select :activity_id_eq, @activities, :id, :name, { include_blank: 'All' } %>
  <%= f.collection_select :city_id_eq, @cities, :id, :name, { include_blank: 'All' } %>
  <%= f.select :start_date_eq, options_for_select([
        ['Today', Time.zone.today],
        ['Tomorrow', Time.zone.tomorrow],
        [(Time.zone.today + 2).strftime("%d %b"), Time.zone.today + 2],
        [(Time.zone.today + 3).strftime("%d %b"), Time.zone.today + 3],
        [(Time.zone.today + 4).strftime("%d %b"), Time.zone.today + 4],
        [(Time.zone.today + 5).strftime("%d %b"), Time.zone.today + 5],
        [(Time.zone.today + 6).strftime("%d %b"), Time.zone.today + 6]
      ], @q.start_date_eq), { include_blank: true } %>
  <%= f.time_select :start_time_gteq, ampm: true, default: { hour: '07' } %>
  <%= f.time_select :end_time_lteq, ampm: true, default: { hour: '23', minute: '45' } %>

  <%= f.submit "Filter" %>
<% end %>

不幸的是,当我输入一些代码来处理时区时,过滤器不再起作用:

# application.rb
default settings, no changes

# application_controller.rb
before_filter :set_time_zone

def set_time_zone
  if current_user
    Time.zone = current_user.time_zone
  elsif current_admin
    Time.zone = current_admin.time_zone
  end
end

如果我只过滤城市,我得到这个查询:

SELECT "schedules".* 
FROM "schedules" 
WHERE (
    "schedules"."city_id" = 6 AND 
    starts_at::time >= '2015-09-30 23:00:00.000000' AND 
    ends_at::time <= '2015-10-01 15:45:00.000000'
) AND (
    starts_at >= '2015-10-01 20:52:39.866880'
) ORDER BY "schedules"."starts_at" ASC, "schedules"."ends_at" ASC`

基本上,如果用户的时区是 GMT+8,那么它将使用今天的日期(2015 年 10 月 1 日)从 starts_atends_at 时间 select 菜单中获取值,并且在 运行 查询之前自动减去 8 小时。如何防止 Ransack 减去偏移小时数?

显然,sql 查询根据我的时区自动减去 8 小时是预期的行为,也是正确的做法。

我没有过滤不涉及任何日期的数据,而是从日期 select 中删除了空白选项,并确保显示的默认计划被过滤到明天的日期,而不是显示所有计划。这样,用户必须先选择一个日期,然后才能过滤时间表。

我必须修改 select 时间的传入参数,以便在将所有参数交给 Ransack 之前使用日期 select 的参数。

排程模型:

# Delete the custom Ransack methods as we don't need them anymore:

ransacker :start_date, type: :date ...
ransacker :start_time, type: :time ...
ransacker :end_time, type: :time ...

# ...and add these scopes:

scope :sort_by_datetime_asc, -> { order(:starts_at, :ends_at) }

def self.only_tomorrow
  where('starts_at BETWEEN ? AND ?', Time.zone.tomorrow.beginning_of_day, Time.zone.tomorrow.end_of_day)
end

调度控制器:

params[:q] = {} unless params[:q]

# Copies the date params from date select to replace the date params from the time select menu.
if params["start_date"].present?
  date  = Time.zone.parse(params["start_date"])

  year  = date.strftime("%Y")
  month = date.strftime("%m")
  day   = date.strftime("%e")

  params[:q]["starts_at_gteq(1i)"] = year
  params[:q]["starts_at_gteq(2i)"] = month
  params[:q]["starts_at_gteq(3i)"] = day

  params[:q]["ends_at_lteq(1i)"]   = year
  params[:q]["ends_at_lteq(2i)"]   = month
  params[:q]["ends_at_lteq(3i)"]   = day
end

@q = Schedule.ransack(params[:q])

if params[:q].present?
  @schedules = @q.result
else
  @schedules = Schedule.only_tomorrow
end

@schedules = @schedules.six_hours_from_now.sort_by_datetime_asc

日程表#index 视图:

<%= select_tag "start_date", options_for_select([...], selected: default_date_select) %>
<%= f.time_select :starts_at_gteq ...
<%= f.time_select :ends_at_lteq ...

日程安排助手:

# Show selected date for Schedule's date filter.
def default_date_select
  if params["start_date"].present?
    params["start_date"]
  else
    ['Tomorrow', Time.zone.tomorrow]
  end
end