2013-03-27 48 views
4

这是我的解决方案。有更紧凑的吗?In Rails 3如何解析ISO8601日期字符串以获取TimeWithZone实例?

> 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

查看documentationActiveSupport::TimeWithZone。简短的回答:使用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 
+0

不幸的是,这不是答案。我希望TimeWithZone实例在IS中的时区已经包含在我从客户端获得的iso8601字符串中。当客户端将时区作为另一个参数传递时,您的解决方案将工作。请注意,在第一次解析时,您已经丢失了时区信息。 – 2013-03-28 04:45:04

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/241/6,你会得到的1不正确的timezone_offset。因此,我调整了你的实现并使用了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认为这是六月,所以调整到大西洋夏令偏移, -03:00


值得一提的是,甚至没有,如果你能解释日光,并获得标准偏移传递给ActiveSupport::TimeZone[],你仍然没有正确的区域,因为偏移区域映射ISN”一对一。

由于这里证明:

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库,它可以使用区域几何做区域查找的位置。

+0

谢谢,这是一个很好的答案!它应该是被接受的,恕我直言。 – Magne 2017-01-24 09:06:29