19

只是增加了关于时区和 DST 问题的数百万个问题。

我有一个带有单独日期和时间字段的表单,我将它们组合起来创建一个 DateTime 像这样

start_time = DateTime.parse("#{parse_date(form_date)} #{form_start_time} #{Time.zone}")

如果我在 2012 年 8 月 21 日和 15:00 填写表格,那么这些就是我在重新加载表格时看到的值。如果我在我的模型中查看我的 start_time 属性,它被正确设置为Tue, 21 Aug 2012 15:00:00 EST +10:00.

如果我在夏令时开始后使用今年晚些时候的日期(我在澳大利亚),则会出现我遇到的问题。如果我使用 2012 年 12 月 21 日和 15:00,请检查我看到的 start_time Fri, 21 Dec 2012 16:00:00 EST +11:00

我对问题的解释是日期保存在我当前的时区 (+10:00) 中,因为这是我告诉 DateTime.parse 要做的。但是,当返回值时,Rails 会查看日期并说“嘿,现在是 12 月的夏令时”并返回 +11:00 时区的时间。

如果 DST 生效,我想要做的是告诉 DateTime.parse 将时间保存在 +11:00 时区。显然将 Time.zone 传递到我的字符串中并不能实现这一点。有没有一种简单的方法可以做到这一点?我可以看到使用 Time#dst 的方法吗?但我怀疑这会创建一些非常难看的复杂代码。我认为可能有一种我缺少的内置方式。

4

6 回答 6

25

(Rails 4.2.4 的答案,未检查旧版本或新版本)

我建议不要使用固定班次 +01:00、+02:00 等,而是使用in_time_zone带有时区名称作为参数的 String 方法:

夏季时间:

ruby :001 > "2016-07-02 00:00:00".in_time_zone('Paris')
 => Sat, 02 Jul 2016 00:00:00 CEST +02:00 

冬季时间:

ruby :002 > "2016-11-02 00:00:00".in_time_zone('Paris')
 => Wed, 02 Nov 2016 00:00:00 CET +01:00 

String#in_time_zone相当于:

ruby :003 > Time.find_zone!("Paris").parse("2016-07-02 00:00:00")
 => Sat, 02 Jul 2016 00:00:00 CEST +02:00 

ruby :004 > Time.find_zone!("Paris").parse("2016-11-02 00:00:00")
 => Wed, 02 Nov 2016 00:00:00 CET +01:00 

您可以通过以下方式获取时区名称:

$ rake time:zones:all

或在 Rails 控制台中:

ruby :001 > ActiveSupport::TimeZone.all.map(&:name)

或为选择标签构建集合:

ActiveSupport::TimeZone.all.map do |timezone|
  formatted_offset = Time.now.in_time_zone(timezone.name).formatted_offset
  [ "(GMT#{formatted_offset}) #{timezone.name}", timezone.name ]
end

并存储时区名称而不是班次。

注意:不要混淆String#in_time_zone方法和Time#in_time_zone方法。

考虑我系统的时区是“巴黎”。

ruby :001 > Time.parse("2016-07-02 00:00:00")
 => 2016-07-02 00:00:00 +0200 
ruby :002 > Time.parse("2016-07-02 00:00:00").in_time_zone("Nuku'alofa")
 => Sat, 02 Jul 2016 11:00:00 TOT +13:00 
于 2016-12-06T18:03:02.013 回答
12

到目前为止,这是我的解决方案。我希望有人有更好的。

start_time = DateTime.parse "#{date} #{(form_start_time || start_time)} #{Time.zone}"
start_time = start_time - 1.hour if start_time.dst? && !Time.now.dst?
start_time = start_time + 1.hour if Time.now.dst? && start_time.dst?

它似乎有效,但我没有严格测试它。我怀疑它可以被美化和缩短,但我认为这是可读和可以理解的。有什么改进吗?

于 2012-08-22T02:28:32.410 回答
2

我遇到了这个确切的问题。我的应用程序允许用户查看即将发生的事件。在美国,我们在 11 月 2 日进入 DST,并且该日期及之后的所有事件都提前一小时显示时间。

我们需要有机会选择时区并将其存储到自己的字段中。在我使用以下内容存储我的日期时间之前:

timezone_offset = Time.now.in_time_zone(params[:opportunity][:time_zone]).strftime("%z") #-0700
DateTime.parse("#{params[:opportunity][:start_datetime]} #{timezone_offset}")

为了解决这个问题,我已更改为:

start_datetime = Time.zone.parse(params[:opportunity][:start_datetime])

要显示正确的时间,我们使用:

@opportunity.start_datetime.in_time_zone(@opportunity.time_zone)
于 2014-10-02T06:23:41.770 回答
0

我会尝试使用

Australian Eastern Standard Time (AEST) (UTC +10).  
Australian Central Standard Time (ACST) (UTC +9 ½).  
Australian Western Standard Time (AWST) (UTC +8).  

调整夏令时。

于 2012-08-22T03:11:32.893 回答
0

在 Rails 中,我们可以使用 ActiveSupport::TimeZone:

tz = ActiveSupport::TimeZone.new 'Pacific Time (US & Canada)'
tz.parse(date_str_without_zone).to_datetime

我使用TZip从邮政编码中获取 TimeZone 字符串(例如“太平洋时间(美国和加拿大)”)。

于 2014-09-17T21:54:10.953 回答
0

如果您有自定义的日期/时间格式,不同于 所支持的String#in_time_zone,您还可以使用(自 rails 5 起)strptime,例如:

Time.find_zone!('Auckland').strptime('2021-02-02 08.00.00', '%Y-%m-%d %H.%M.%S')
于 2021-03-18T00:31:07.737 回答