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

时间:2015-10-01 15:04:32

标签: ruby-on-rails datetime rails-postgresql ransack

在我的设置中,我想使用选择菜单按ScheduleActivityCityDateStarting Time过滤End Time

我已经与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时间选择菜单中的值,并自动减去8小时在运行查询之前。如何防止Ransack减去偏移小时数?

1 个答案:

答案 0 :(得分:0)

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

我没有在没有任何日期的情况下过滤数据,而是从日期选择中删除了空白选项,并确保显示的默认计划被过滤到明天的日期,而不是显示所有日程表。这样,用户必须在过滤日程表之前先选择日期。

我必须从时间选择中修改传入的参数,因此在将所有参数传递给Ransack之前,它使用日期选择中的参数。

日程安排模型:

# 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

Schedule #index view:

<%= 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