2013-03-27 5 views
4

Вот мое решение. Есть ли более компактный?В 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

Отъезд documentation для ActiveSupport::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, которую я получаю от клиента. Ваше решение будет работать, когда клиент передает часовой пояс в качестве других параметров. Обратите внимание, что при первом анализе вы уже потеряли информацию о часовом поясе. –

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. Таким образом, я изменил вашу реализацию и использовал 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» t один к одному.

Как показано здесь:

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 строки.

Кстати, если вы преследуете такой подход, я рекомендую библиотеку Node.js tzwhere, которая может использовать геометрию зон, чтобы сделать поиск местоположения в зоне.

+0

Спасибо, это отличный ответ! Это должно быть принято, имхо. – Magne

 Смежные вопросы

  • Нет связанных вопросов^_^