2012-12-15 87 views
3

我必須顯示在過去的30天裏每天有多少次api調用,以便構建一個不錯的圖表。到目前爲止,我沒有任何問題。需要注意的一點是,數據庫和系統時區採用UTC,但大多數用戶位於太平洋時間(GMT -8)。按小時分組並按軌道使用用戶時區?

爲了獲得通過使用當天分組的API的使用,我可以這樣做

ApiCall. 
    where(user_id: @user.id). 
    where("requested_at > ?", Time.zone.now.to_date - 30.days). 
    group("date(requested_at)"). 
    select("count(*) as qty, date(requested_at) as requested_at").all 

現在的問題是,對位於加利福尼亞用戶的時間,是從遙遠的不同時間爲位於英格蘭的用戶,這是我失敗的地方。

用戶在2012-12-14 21:00:00 GMT -8發出位於加州的API調用。數據庫中的API CALL與時間2012-12-14 05:00:00一起存儲(因爲它在格林尼治標準時間,所以它增加了8個小時)。

現在對於那個用戶來說,如果我去查看我的日常用法,用我做的查詢,它會顯示我的api調用不是在2012年12月14日21:00:00進行的,因爲在數據庫它的存儲爲2012-12-14 05:00:00,對於用戶來說,api的使用情況將顯示他在第二天做了api調用,他會認爲系統工作不正常。

因此,我怎麼能根據用戶時區對基於數據庫時區的api調用進行分組?

回答

2

是有,通過使用MySQL的轉換時區功能,它不是一個很好的解決方案但它的工作原理

ApiCall. 
    where(user_id: @user.id). 
    where("requested_at > ?", Time.zone.now.to_date - 30.days). 
    group("date(convert_tz(requested_at, '+00:00', '-08:00'))"). 
    select("count(*) as qty, date(requested_at) as requested_at").all 

我將在後面添加一個賞金,看看是否有人能告訴我一個更好的解決方案,以這些種類的導軌和時區報告

2

您可以設置請求的時區並在Rails中進行日期排序。

ApiCall. 
    where(user_id: @user.id). 
    where("requested_at > ?", Time.zone.now.to_date - 30.days). 
    group_by({|api_call| api_call.requested_at.to_date}) 

通常在Rails中,您可以在控制器操作的before_filter中設置時區。

Time.zone = current_user.time_zone if logged_in? 
+0

是的,我知道時間.zone和我知道使用紅寶石分組記錄,但離開數據庫引擎進行分組更有效,所以我真正想要的是一個很好的方式來處理時區,同時讓引擎做複雜的操作。 – rorra

2

我不認爲Rails有這樣的範圍,將轉換DB日期在用戶時區做一個「組」在它之前。

但是,你可以定義自己的範圍來處理,例如:

class ApiCall 
    scope :group_by_date_in_timezone, lambda{|date_column| 
    group("date(convert_tz(#{date_column}, '+00:00', '#{Time.zone.formatted_offset}'))") 
    } 

然後將查詢變得更清楚一點:

ApiCall. 
    where(user_id: @user.id). 
    where("requested_at > ?", Time.zone.now.to_date - 30.days). 
    group_by_date_in_timezone(:requested_at). 
    select("count(*) as qty, requested_at").all 

    # 'date(requested_at) as requested_at' would be also in DB Time zone 
    # Instead we can select 'requested_at' to let Rails do the user timezone conversion 
    # you can easily convert it to a date later with #to_date 

此範圍還可能對其他模型有用,那麼你可以將其定義爲一個全局的ActiveRecord範圍,在這個問題的答案之後:Hacking ActiveRecord: add global named scope

-1

我認爲這是一個很好的做法,以UTC存儲所有記錄,所以不需要轉換任何內容,並且將正確計算時間內部值。在向用戶顯示時間時僅轉換爲用戶的本地時區。晚

+1

我已經以UTC存儲了所有內容,而當我必須按天或小時進行分組時,問題就出現了,因爲用戶有不同的時區。 – rorra

+0

好了,在這種情況下,很容易,因爲一切都在UTC,當用戶查詢他在某一天(當地時間)所做的api調用時,只需將他的本地時間轉換爲db中的UTC時間等值,構建查詢。 在你的例子中,用戶的api調用自他當地時間12/14 21:00以後將成爲db的UTC時間,因爲12/15 5:00 – lionel

+0

但是這讓我回到原來的問題。假設你位於洛杉磯,所以它的GMT-8。數據庫以GMT + 0格式存儲日期和時間。你昨天晚上10點打了50個電話,今天凌晨4點又打了50個電話。分貝存儲在今天,第一個在上午6點,其他人在下午12點。如果我按天分組,則數據庫將與時區格林尼治標準時間+0組合,所以它會說你今天做了100個API調用,但我需要它說你今天做了50個API調用並且做了正確的時區轉換,而我正在尋找一種更乾淨的方式來利用軌道時區。 – rorra

1

一點點,但是這裏來PostgreSQL的答案:

首先定義這個變種使事情變得更加易讀:

requested_at_day = "date(requested_at AT TIME ZONE \'UTC\' 
       AT TIME ZONE \'#{Time.zone.tzinfo.identifier}\')" 

然後:

ApiCall. 
    where(user_id: @user.id). 
    where("requested_at > ?", Time.zone.now.to_date - 30.days). 
    group(requested_at_day). 
    select("count(*) as qty, #{requested_at_day} as requested_at_day").all