2013-06-20 41 views
3

我试图显示特定用户时区中过去30天的每日展示次数。问题在于,根据时区的不同,计数并不总是相同的,而且我在查询中遇到了麻烦。Rails/Postgres查询按时区分组的日期行

例如,在第一天在CDT(-5)下午11点发生两次印象,在CDT上午1:00发生一次印象。如果您使用UTC(+0)进行查询,您将在第二天获得全部3次展示,而不是第一天和第二天两次。 CDT时间都在UTC的第二天降落。

这是我现在在做什么,我知道我必须缺少的东西在这里简单:

start = 30.days.ago 
finish = Time.now 

# if the users time zone offset is less than 0 we need to make sure 
# that we make it all the way to the newest data 
if Time.now.in_time_zone(current_user.timezone) < 0 
    start += 1.day 
    finish += 1.day 
end 

(start.to_date...finish.to_date).map do |date| 
    # get the start of the day in the user's timezone in utc so we can properly 
    # query the database 
    day = date.to_time.in_time_zone(current_user.timezone).beginning_of_day.utc 
    [ (day.to_i * 1000), Impression.total_on(day) ] 
end 

印象型号:

class Impression < ActiveRecord::Base 
    def self.total_on(day) 
    count(conditions: [ "created_at >= ? AND created_at < ?", day, day + 24.hours ]) 
    end 
end 

我一直在寻找其他的职位,它好像我可以让数据库为我处理很多繁重的工作,但我没有使用像AT TIME ZONEINTERVAL这样的任何东西。

我没有看起来真的很脏,我知道我必须错过一些明显的东西。任何帮助表示赞赏。

+0

目前还不清楚事情是如何存储在数据库中。没有时区的时间戳?带时区的时间戳?时区是在应用程序或数据库级别进行标准化的吗?你甚至确定他们存储正确吗? (例如,你可能在没有时区的情况下存储它们,并且服务器在追加它自己的时区后将它们存储起来) –

+0

我使用的是created_at,其中rails默认设置在http://api.rubyonrails.org/classes/ActiveRecord/ Timestamp.html。它们存储在UTC中。我应该补充说,这是因为我的服务器的本地时区是UTC。 – ifightcrime

+0

Err ...换句话说,他们在时区utc时区带时区? –

回答

2

好吧,从this awesome article有点帮助我想我已经明白了。我的问题源于不知道系统Ruby时间方法和时区感知Rails方法之间的差异。一旦我使用around_filter like this为用户设置正确的时区,我能够使用内置的Rails的方法来简化代码颇有几分:

# app/controllers/application_controller.rb 

class ApplicationController < ActionController::Base 
    around_filter :set_time_zone 

    def set_time_zone 
    if logged_in? 
     Time.use_zone(current_user.time_zone) { yield } 
    else 
     yield 
    end 
    end 
end 

# app/controllers/charts_controller.rb 

start = 30.days.ago 
finish = Time.current 

(start.to_date...finish.to_date).map do |date| 
    # Rails method that uses Time.zone set in application_controller.rb 
    # It's then converted to the proper time in utc 
    time = date.beginning_of_day.utc 
    [ (time.to_i * 1000), Impression.total_on(time) ] 
end 

# app/models/impression.rb 

class Impression < ActiveRecord::Base 
    def self.total_on(time) 
    # time.tomorrow returns the time 24 hours after the instance time. so it stays UTC 
    count(conditions: [ "created_at >= ? AND created_at < ?", time, time.tomorrow ]) 
    end 
end 

可能有一些更多的,我可以做,但现在我对此感觉好多了。

1

。假定该around_filter正常工作并设置Time.zone块中,你应该能够查询重构为这样:

class Impression < ActiveRecord::Base 
    def self.days_ago(n, zone = Time.zone) 
    Impression.where("created_at >= ?", n.days.ago.in_time_zone(zone)) 
    end 
end