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(因为它在 GMT 中,所以它增加了 8 小时)。

现在对于那个用户,如果我去查看我的日常使用情况,通过我所做的查询,它将显示我的 api 调用不是在 2012 年 12 月 14 日 21:00:00 进行的,因为它在数据库上存储为2012-12-14 05:00:00,对于用户而言,api 使用情况将显示他在第二天进行了该 api 调用,他会认为系统工作不正常。

简而言之,如何根据用户时区而不是数据库时区对 api 调用进行分组?

4

5 回答 5

2

是的,通过使用 mysql convert timezone 函数,它不是一个很好的解决方案,但它可以工作

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

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

于 2012-12-16T13:42:27.603 回答
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?
于 2012-12-16T17:15:26.397 回答
2

我不认为 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

于 2012-12-20T10:03:19.110 回答
1

有点晚了,但是 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
于 2015-09-24T21:45:52.883 回答
-1

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

于 2012-12-23T23:53:37.297 回答