gpt4 book ai didi

ruby-on-rails - 在 rails 3.2 中存储带时区的时间戳

转载 作者:数据小太阳 更新时间:2023-10-29 06:50:12 26 4
gpt4 key购买 nike

我正在尝试将所有时间戳及其包含的时区存储在 Rails 应用程序中。我对 ActiveRecord 将它们转换为 utc 很好,但是我有多个应用程序访问同一个数据库,其中一些应用程序是根据时区要求实现的。所以我想做的是像往常一样让 activerecord 转换我的时间戳,然后将它们写入数据库,并在时间戳后附加字符串“America/Los_Angeles”或任何适当的时区。我目前在 jruby 1.7.8 上运行 rails 3.2.13,它实现了 ruby​​ 1.9.3 api。我的数据库是 postgres 9.2.4,与 activerecord-jdbcpostgresql-adapter gem 连接。列类型是带时区的时间戳。

我已经使用 activerecord-native_db_types_override gem 更改了自然的 activerecord 映射,方法是将以下行添加到我的 environment.rb:

  NativeDbTypesOverride.configure({
postgres: {
datetime: { name: "timestamp with time zone" },
timestamp: { name: "timestamp with time zone" }
}
})

我的application.rb目前包含

  config.active_record.default_timezone = :utc
config.time_zone = "Pacific Time (US & Canada)"

我怀疑我可以重写 ActiveSupport::TimeWithZone.to_s 并更改它的 :db 格式以输出正确的字符串,但我还不能做到这一点。非常感谢任何帮助。

最佳答案

在为同样的问题苦苦思索之后,我了解到了这件事的可悲真相:

Postgres 不支持在其任何日期/时间类型中存储时区

因此,您根本无法在一列中同时存储单个时刻及其时区。在我提出替代解决方案之前,让我用 Postgres docs 来支持它:

All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.

所以没有什么好的方法可以简单地将时区“附加”到时间戳。但这并没有那么可怕,我保证!这只是意味着您需要另一列。

我(相当简单)提出的解决方案:

  1. 将时区存储在字符串列中(我知道很恶心)。
  2. 不要覆盖 to_s,只需编写一个 getter。

假设您在 explodes_at 列上需要这个:

def local_explodes_at
explodes_at.in_time_zone(self.time_zone)
end

如果你想自动存储时区,覆盖你的setter:

def explodes_at=(t)
self.explodes_at = t
self.time_zone = t.zone #Assumes that the time stamp has the correct offset already
end

为了确保 t.zone 返回正确的时区,需要将 Time.zone 设置为正确的时区。您可以使用 an around filter (Railscast) 为每个应用程序、用户或对象轻松改变 Time.zone .有很多方法可以做到这一点,我喜欢 Ryan Bates 的方法,因此请以对您的应用程序有意义的方式实现它。

如果你想变得更有趣,并且你需要在多个列上使用这个 getter,你可以遍历所有列并为每个日期时间定义一个方法:

YourModel.columns.each do |c|
if c.type == :datetime
define_method "local_#{c.name}" do
self.send(c.name).in_time_zone(self.time_zone)
end
end
end

YourModel.first.local_created_at #=> Works.
YourModel.first.local_updated_at #=> This, too.
YourModel.first.local_explodes_at #=> Ooo la la

这不包括 setter 方法,因为您真的不希望每个日期时间列都能够写入 self.time_zone。您必须决定在哪里使用它。如果您想要真正喜欢,您可以通过在模块中定义它并将其导入每个模型来在所有模型中实现它。

module AwesomeDateTimeReader
self.columns.each do |c|
if c.type == :datetime
define_method "local_#{c.name}" do
self.send(c.name).in_time_zone(self.time_zone)
end
end
end
end

class YourModel < ActiveRecord::Base
include AwesomeDateTimeReader
...
end

这是一个相关的有用答案:Ignoring timezones altogether in Rails and PostgreSQL

希望这对您有所帮助!

关于ruby-on-rails - 在 rails 3.2 中存储带时区的时间戳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20671866/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com