使用时区时过滤日期时间不起作用
Filtering datetime not working when using timezone
在我的设置中,我想通过 Activity
、City
、Date
、Starting Time
和 End 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_at
和 ends_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
在我的设置中,我想通过 Activity
、City
、Date
、Starting Time
和 End 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_at
和 ends_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