如何在 Ruby 中更改 DateTime 的时区?

Posted

技术标签:

【中文标题】如何在 Ruby 中更改 DateTime 的时区?【英文标题】:How do I alter the timezone of a DateTime in Ruby? 【发布时间】:2011-05-26 22:48:11 【问题描述】:

在保持时区不变的同时读取、写入和序列化日期和时间变得很烦人。我正在使用 Ruby(和 Rails 3.0)并试图更改 DateTime 的时区。 (到 UTC)但不是时间本身。

我想要这个:

t = DateTime.now
t.hour
-> 4
t.offset = 0
t.hour
-> 4
t.utc?
-> true

我最接近的是这个,但它不直观。

t = DateTime.now
t.hour
-> 4
t += t.offset
t = t.utc
t.hour
-> 4
t.utc?
-> true

有没有更好的办法?

【问题讨论】:

如果您实际上使用“现在”作为时间戳,那么将其作为同一时间的 UTC 标记版本存储在数据库中是一个谎言,对吗?当系统的所有部分都使用 UTC 时,我个人遇到的麻烦最少,并且只有在视图中我可能会调整以显示在服务器或浏览器的时区中。 这很困难,因为我正在从时区未知或不相关的 CSV 文件中读取日期,因此我将它们作为 UTC 存储在数据库中。但是当使用本地区域的日期(即大于2010/01/01 8:00 pm)进行查询时,它首先将该时间转换为UTC(我的问题的根源),然后再在查询中使用它。我试图让整个应用程序与时区无关,以便用户可以加载时间在 7:00 - 8:00 范围内的 CSV 文件,然后查询 7:00 - 8:00 并让它返回同一组记录。相反,Ruby 假定解析的时间在本地区域中。 【参考方案1】:

使用 Rails 3,您正在寻找 DateTime.change()

dt = DateTime.now
=> Mon, 20 Dec 2010 18:59:43 +0100
dt = dt.change(:offset => "+0000")
=> Mon, 20 Dec 2010 18:59:43 +0000

【讨论】:

不管怎样,.change 似乎也可以在 2.3 中使用。 更有用的是实际链接到答案。我想是这个:***.com/questions/5941613/… 或简单的 DateTime.now.utc DateTime.now.utc 将更改时间。 注意:这将改变所表示的绝对时间。【参考方案2】:
d = DateTime.now
puts [ d, d.zone ]
#=> 2010-12-17T13:28:29-07:00
#=> -07:00

d2 = d.new_offset(3.0/24)
puts d2, d2.zone
#=> 2010-12-17T23:28:29+03:00
#=> +03:00

编辑:此答案未说明对另一个答案的评论提供的信息,即希望在时区更改后报告的“小时”相同。

【讨论】:

new_offset 听起来很有用,文档使它看起来像是我所追求的,但它只是从一个偏移量转换为另一个偏移量。正如问题所述,我希望更改偏移量,同时不理会时间。 对我来说,DateTime.now.new_offset(-8.0/24) 是这个页面上唯一对我有用的东西——大约进行一次银行交易。从太平洋时间开始,DST 可以忽略不计。可能是因为我的简单脚本不包含提到的框架或 Active 任​​何东西。【参考方案3】:

使用数字偏移量,例如由于夏令时,'+1000' 不会全年工作。签出 ActiveSupport::TimeZone。 More info here

【讨论】:

最好提供一个使用ActiveSupport::TimeZone 的示例,而不仅仅是链接到文档。 @Sam OP 询问有关“转换”为 UTC 的问题。如果我们有一个有效的时间戳和有效的偏移量,我们可能会遇到问题吗?【参考方案4】:

正如 @Sam 指出的那样,更改偏移量是不够的,并且会导致错误。为了抵抗 DST 时钟提前,转换应该通过以下方式完成:

d = datetime_to_alter_time_zone
time_zone = 'Alaska'

DateTime.new
  .in_time_zone(time_zone)
  .change(year: d.year, month: d.month, day: d.day, hour: d.hour, min: d.min, sec: d.sec)

【讨论】:

【参考方案5】:

我会改用Time 对象。因此,获取当前本地时间,然后将其增加 UTC 偏移量并转换为 UTC,如下所示:

t = Time.now # or Time.parse(myDateTime.asctime)
t # => Thu Dec 16 21:07:48 -0800 2010
(t + t.utc_offset).utc # => Thu Dec 16 21:07:48 UTC 2010

尽管根据 Phrogz 的评论,如果您只想以独立于位置的方式存储时间戳,那么只需使用当前的 UTC 时间:

Time.now.utc

【讨论】:

这是一个很大的性能打击。这似乎比我上面已有的解决方案更慢且更复杂。 如果您并不真正关心 DateTime 对象(只是当前时刻),则跳过解析并使用Time.now。我已经稍微编辑了我的答案。 这和我上面的解决方案完全一样,但是多了一个字符串解析步骤。【参考方案6】:

如果有人(比如我)有时区为字符串格式,如“太平洋时间(美国和加拿大)”

这是我发现的最好方法:

datetime.change(ActiveSupport::TimeZone[time_zone_string].formatted_offset(false))

【讨论】:

是否包括夏令时? @new2cpp 你是什么意思,当我们想保持时间和一切都一样,只想换出时区时,我认为夏令时不适用于这里。那是一个不同的问题。【参考方案7】:

您可以通过在配置文件 (config/application.rb) 中添加以下行来指定要在应用中使用的时区: config.time_zone = '孟买'

您可以在此处找到相同的官方文档:http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

另一个选择是你“猴子补丁”'DateTime'类。

【讨论】:

以上是关于如何在 Ruby 中更改 DateTime 的时区?的主要内容,如果未能解决你的问题,请参考以下文章

如何更改 asp.net 应用程序的时区

如何在 python 中使用带有 datetime 对象的时区?

如何在 python 中使用带有 datetime 对象的时区?

带时区的Strptime

如何在特定时区转换 DateTime?

如何在特定时区转换 DateTime?