Rails范围中与时区无关的截止日期比较

时间:2018-08-25 18:35:07

标签: ruby-on-rails postgresql datetime activerecord timezone

请考虑以下情况。我有一个带有日期时间 activation_due_date 字段的 Voucher 模型和一个具有关于其位置(时区,UTC偏移)的最新信息的用户模型。
我想检查他是否在任何可用时区之前要求激活凭证。例如,如果到期日期设置为28.08.2018 23:59 UTC 我希望我的作用域before_activation_due检查他是否在当前时区2018年8月28日-23:59之前提出要求,所以我的到期日期不是固定的-它取决于用户的位置-在一个地方可以在到期日之后,也可以在其他日期之前。
我尝试了以下方法。

models / voucher.rb

scope :before_activation_due, lambda { |user|
  where('activation_due_date > ? ', Time.current.to_utc + user.utc_offset)
}

我的问题是:

  1. 这是正确的方法吗?如果没有,处理此类案件的正确方法是什么?

  2. 如何测试这种范围?当前时间戳可能是在查询执行期间比较日期时间时从数据库服务器获取的,因此我不确定如何在规格中对其进行模拟。

谢谢。

2 个答案:

答案 0 :(得分:3)

您可以仅存储用户的时区,而不存储偏移量,然后执行以下操作:

where('activation_due_date > ? ', Time.now.utc.in_time_zone(user.timezone))

时区是

中显示的任何有效时区
rake time:zones

那至少是做事更轻松的方式。但是我不认为存储偏移量然后手动将其添加到时间中是一种不好的方法。

要对此进行测试,可以将所需的任何日期手动插入数据库中。然后,您可以使用https://github.com/travisjeffery/timecop之类的宝石及时到达该点,并测试您的范围:

Voucher.create(activation_due_date: '2018-01-02 00:00:00')
format = '%Y-%m-%d %H:%M:%S %Z'
time = DateTime.strptime("2018-01-02 00:00:00 Central Time (US & Canada)",format)
Timecop.travel(time)
Voucher.before_activation_due.all ...

答案 1 :(得分:1)

一种方法是将activation_due_date转换为用户的时区。正如您所说的:“我的到期日不是固定的-它取决于用户的位置”。

要将此操作作为作用域,最简单的方法是使用数据库的时区功能。这取决于您使用的数据库,但是在PostgreSQL中,它将类似于:-

where('activation_due_date AT TIME ZONE ? > NOW() ', user.timezone)


一种更简单的方法是进行字符串比较

where('to_char(activation_due_date, 'YYYYMMDDHH24MISS') > ?', Time.current.in_time_zone(user.timezone).strftime('%Y%m%d%H%M%S');

在这种情况下,我们要说的是用户在墙上的时间是什么,它比数据库中的时间短(在UTC中“存储”的时间)。