【问题标题】:How do I alter the timezone of a DateTime in Ruby?如何在 Ruby 中更改 DateTime 的时区?
【发布时间】: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 假定解析的时间在本地区域中。

标签: ruby-on-rails ruby datetime timezone ruby-on-rails-3


【解决方案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 中使用。
  • 更有用的是实际链接到答案。我想是这个:stackoverflow.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'类。

    【讨论】:

      猜你喜欢
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      • 2015-08-04
      • 2010-12-23
      • 2014-11-14
      • 1970-01-01
      • 2020-12-25
      • 2015-10-26
      相关资源
      最近更新 更多