2013-06-20 38 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