【问题标题】:Group by hour and use user time zone in rails?按小时分组并在rails中使用用户时区?
【发布时间】:2015-09-25 05:45:52
【问题描述】:

我必须显示过去 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 调用进行分组?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 activerecord


    【解决方案1】:

    是的,通过使用 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
    

    稍后我将添加赏金,看看是否有人可以告诉我有关此类带有 Rails 和时区的报告的更好解决方案

    【讨论】:

      【解决方案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?
      

      【讨论】:

      • 是的,我知道 Time.zone 并且我知道使用 ruby​​ 对记录进行分组,尽管让数据库引擎进行分组更有效,所以我真正在寻找什么是一种很好的使用时区的 Rails 方式,同时让引擎执行复杂的操作。
      【解决方案3】:

      我认为 Rails 没有这种范围,它会在对用户时区进行“分组”之前转换数据库日期。

      但是,您可以定义自己的范围来处理它,例如:

      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

      【讨论】:

        【解决方案4】:

        有点晚了,但是 Postgresql 的答案来了:

        首先定义这个 var 以使内容更具可读性:

        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
        

        【讨论】:

          【解决方案5】:

          我认为以 UTC 格式存储所有记录是一种很好的做法,因此无需转换任何内容,并且可以正确计算时间间隔。仅在向用户显示时间时转换为用户的本地时区。

          【讨论】:

          • 我已经将所有内容都存储在 UTC 中,当我必须按天或按小时分组时,问题就出现了,因为用户有不同的时区。
          • 好吧,在那种情况下很容易,因为一切都是UTC,当用户查询他在某一天(他的当地时间)进行的api调用时,只需将他的当地时间转换为UTC时间数据库中的等效项并使用它来构造查询。在您的示例中,用户从当地时间 12/14 21:00 开始的 api 调用将成为数据库自 12/15 5:00 开始的 UTC 时间
          • 但这让我回到了最初的问题。假设您位于洛杉矶,因此它是 GMT-8。 db 将日期和时间存储在 GMT+0 中。你昨天晚上 10 点打了 50 个 api 调用,今天凌晨 4 点又打了 50 个。 db 都在今天存储,第一个在早上 6 点存储,其他存储在下午 12 点。如果我按天分组,数据库将与时区 GMT+0 分组,所以它会说你今天进行了 100 次 api 调用,但我需要它说你今天进行了 50 次 api 调用并进行了正确的时区转换,我正在寻找一种利用 Rails 时区的更清洁的方法。
          • 这取决于您如何定义“今天”在您的情况下,您需要说用户在“今天”进行了 50 次 API 调用,这意味着您从用户的角度定义了“今天” ,这意味着您必须在处理请求时转换到正确的服务器时间范围。假设用户在 GMT-8 上午 5 点登录,并且他有兴趣查看“今天”他的 api 调用,这将转化为从 GMT+0 8AM ~ 现在(即 GMT+0)查找他的 api 调用的请求下午 1 点)。而且每个用户都会有不同的“今天”,我认为这没关系,因为分析数据是呈现给用户的。
          猜你喜欢
          • 2016-08-03
          • 2013-11-27
          • 1970-01-01
          • 1970-01-01
          • 2019-03-09
          • 2011-08-25
          • 2018-10-07
          • 2019-11-24
          相关资源
          最近更新 更多