我需要帮助尝试在作用域中使用时区。下面是我想要实现的示例。
所有日期都以 UTC 时间存储:
例如。时区 UTC +1 的用户发布了将于 20:00 开始的演出。在演出节目页面上,每个人都会看到这是20:00,这是正确的。我的问题在于我必须"过期"演出,因此在date
过后它们不再出现在搜索中。
到目前为止,我一直在使用示波器;
scope :expired, -> { where('date <= ?', Time.current.to_datetime) }
因此,gig.expired
将显示过去gig.date
的所有演出。
问题是,当用户处于 UTC +1 时区时,对他来说,演出将在 21:00 而不是 20:00 "过期",因为它以 UTC 而不是 UTC+1 过期。
编辑:
我现在在创建时将时区保存为演出模型的属性。我已经在使用地理编码器进行定位,它可以保存坐标。使用时区 gem,我可以查找坐标并提取时区,该时区作为字符串保存到模型中。
例如。在视图中,@gig.timezone
显示Europe/Amsterdam
(或保存的任何时区)
或者<%= @gig.date.in_time_zone(@gig.timezone) %>
显示2016-03-17 21:38:00 +0100
我现在的问题是将其与以前的范围一起使用。如果我尝试;
def self.expired
Gig.where('date < ?', Time.now.in_time_zone("Europe/Amsterdam"))
end
Rails 仍然在 UTC 时间"过期"演出。如何添加演出保存的时区,并在范围内使用它?
我从未使用过时区 gem,但如果您在作用域中使用它遇到困难,您可以考虑简单地在返回所需内容的方法中使用它。
Fwiw,我认为如果您为不同的时区保存不同的时间,您可能会使您的应用程序难以维护。我可以看到向用户显示一个转换为其时区的日期......但它们应该以统一的方式保存。这段代码,如果它依赖于当前用户的时区,将会很挣扎。将苹果与苹果进行比较很重要。
如何保存日期?如果将其保存为 UTC 时间,只需将其与 UTC 时间进行比较即可。
如果你更喜欢使用范围,我会做这样的事情:
scope :expired, -> { where('date < ?', Date.today) }
或者,您可以执行以下操作:
def self.expired
Gig.where('date < ?', Date.today)
end
好的,经过几天的尝试,我想出了一个解决方案。如果有人能做得更好,我很想听听。
在创建演出时,我使用时区 gem 查找时区,并将生成的:string
保存到我在演出模型中创建的 timezone
属性 Longitude
和latitude
可用作属性,因为演出具有在创建表单中设置的位置;
timezone = Timezone.lookup(@gig.latitude, @gig.longitude)
@gig.update_attributes(timezone: timezone)
然后,我在演出模型中添加了另一列gigzonetime
。我将其设置为 gig.date,并减去时区偏移量。
gigzonetime = @gig.date - @gig.date.in_time_zone(@gig.timezone).utc_offset
@gig.update_attributes(gigzonetime: gigzonetime)
在显示和索引页面上,我显示<%= @gig.date %>
以显示它将显示的本地时间,在我的范围内,我将新属性gigzonetime
进行比较,以便它在正确的时间过期。
scope :expired, -> { where('gigzonetime <= ?', Time.current.to_datetime) }
这似乎适用于所有时区(未来和过去)对于夏令时时差,它似乎也可以正常工作。