在Rails 3如何解析ISO8601日期字符串以获取TimeWithZone实例?

时间:2013-03-27 19:49:45

标签: ruby-on-rails date timezone iso8601

这是我的解决方案。有一个更紧凑的吗?

> time_from_client = "2001-03-30T19:00:00-05:00"
 => "2001-03-30T19:00:00-05:00" 

> time_from_client.to_datetime
 => Fri, 30 Mar 2001 19:00:00 -0500 

> timezone_offset = time_from_client.to_datetime.offset.numerator
 => -5 

> tz = ActiveSupport::TimeZone[timezone_offset]
 => (GMT-05:00) America/New_York

> tz.class
=> ActiveSupport::TimeZone 

2 个答案:

答案 0 :(得分:2)

不幸的是,这是不可能的,至少没有变得更聪明。

要理解为什么必须区分时区 UTC偏移

考虑从区域到偏移的映射:您还必须知道相关时间以及区域,以便决定是应用标准偏移还是日光偏移。

走另一条路要困难得多。再次只是偏移是不够的,因为我们不知道该偏移是指标准时间还是日光时间。但是这次我们有鸡/蛋问题:即使我们有时间,我们也需要知道区域,以便查看该时间是标准时间还是白天时间。但是,我们没有这个区域。


这是你的一个有用的例子,你正在使用Fri, 30 Mar 2001 19:00:00,这恰好是标准时间(EST),所以在第一次通过它看起来不错:

> time_from_client = "2001-03-30T19:00:00-05:00"
 => "2001-03-30T19:00:00-05:00" 


> time_from_client.to_datetime
 => Fri, 30 Mar 2001 19:00:00 -0500 

> timezone_offset = time_from_client.to_datetime.offset.numerator
 => -5 

> tz = ActiveSupport::TimeZone[timezone_offset]
 => (GMT-05:00) America/New_York

我们有America/New_York


但是看看如果我们跳到夏令时会发生什么,让我们说,30 Jun 2001 19:00:00。您time_from_client的偏移量组件现在为-04:00,这是纽约(EDT)的日光时间偏移量。

> time_from_client = "2001-03-30T19:00:00-4:00"
 => "2001-06-30T19:00:00-05:00" 


> time_from_client.to_datetime
 => Fri, 30 Jun 2001 19:00:00 -0400 

免责声明:下一步实际上不起作用,因为numerator会将4/24向下舍入到1/6,并且您将timezone_offset 1 1}}。因此我调整了您的实现并使用了utc_offset

> timezone_offset = time_from_client.to_datetime.utc_offset
 => -14400

> tz = ActiveSupport::TimeZone[timezone_offset]
 => (GMT-04:00) Atlantic Time (Canada)

现在可以看到问题,而不是America/New_York我们得到Atlantic Time (Canada)。后者是标准偏移-04:00的区域名称中的一个,因为implementation of ActiveSupport::TimeZone[]只能使用标准utc_offset找到,并且不知道日光。

如果您按照此结论得出结论,您最终会遇到以下违反直觉的parse

> tz.parse "2001-06-30T19:00:00-04:00"
 => Sat, 30 Jun 2001 20:00:00 ADT -03:00

我假设在这里发生的是TimeWithZone看到这是6月,因此调整为大西洋日光偏移-03:00


值得注意的是,即使您可以考虑日光,并获得传递给ActiveSupport::TimeZone[]标准偏移,您仍然没有正确的区域,因为偏移区域映射不是一对一的。

如此处所示:

ActiveSupport::TimeZone.all.select { |z| z.utc_offset == -14400 }
=> [(GMT-04:00) Atlantic Time (Canada), (GMT-04:00) Georgetown, (GMT-04:00) La Paz, (GMT-04:00) Santiago]

这是我认为这是不可能的原因,除非您碰巧有原始ISO 8601字符串的位置信息。

顺便提一下,如果您采用这种方法,我建议使用tzwhere Node.js库,它可以使用区域几何体来区域查找位置。

答案 1 :(得分:1)

查看ActiveSupport::TimeWithZone的{​​{3}}。简短回答:使用Time.parse(time_string).in_time_zone

[9] pry(main)> Time.parse("2001-03-30T19:00:00-05:00")
=> 2001-03-30 19:00:00 -0500
[10] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").class
=> Time
[11] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").in_time_zone
=> Sat, 31 Mar 2001 00:00:00 UTC +00:00
[12] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").in_time_zone.class
=> ActiveSupport::TimeWithZone

如果你想在另一个时区:

[13] pry(main)> Time.parse("2001-03-30T19:00:00-05:00").in_time_zone("America/Los_Angeles")
=> Fri, 30 Mar 2001 16:00:00 PST -08:00