Strptime с часовым поясом

#ruby-on-rails #ruby #timezone #datetimeoffset #strptime

#ruby-on-rails #ruby #Часовой пояс #datetimeoffset #strptime

Вопрос:

У меня есть строка, с которой я разбираю DateTime.strptime . Часовой пояс даты в строке — CET, но Ruby создает объект UTC DateTime, который, конечно, имеет смещение в 2 часа.

В настоящее время я работаю над решением проблемы, DateTime.strptime().change(:offset => " 0020") но я почти уверен, что это не так, как это должно работать.

Может кто-нибудь просветить меня о правильном способе сделать это?

Комментарии:

1. если вы находитесь в стране с часовым поясом CEST / CET, фиксированное смещение не будет работать. Также интересует это, я не вижу, как заставить strptime анализировать текущий часовой пояс (но не значение прямо сейчас, правильное в зависимости от анализируемой строки).

Ответ №1:

Я использую его следующим образом:

 ruby-1.9.2-head :001 > require 'date'
 => true 
ruby-1.9.2-head :002 > fmt = "%m-%d-%Y %H:%M:%S %Z"
 => "%m-%d-%Y %H:%M:%S %Z" 
ruby-1.9.2-head :003 > DateTime.strptime "10-26-2011 10:16:29 CET", fmt
 => #<DateTime: 2011-10-26T10:16:29 01:00 (212186380589/86400,1/24,2299161)> 
ruby-1.9.2-head :004 > DateTime.strptime "10-26-2011 10:16:29 UTC", fmt
 => #<DateTime: 2011-10-26T10:16:29 00:00 (212186384189/86400,0/1,2299161)> 
ruby-1.9.2-head :005 > DateTime.strptime "10-26-2011 10:16:29 PST", fmt
 => #<DateTime: 2011-10-26T10:16:29-08:00 (212186412989/86400,-1/3,2299161)> 
 

Это то, что ты имеешь в виду?

Комментарии:

1. у меня нет строки, определяющей часовой пояс в моей анализируемой строке. его 10-26-2011 10:16:29 вместо «10-26-2011 10:16:29 CET»

2. Вы решили мою проблему, я могу просто "09-19-2012 11:08:44" " CST" . Я думаю, что ваш ответ правильный.

3. А как насчет того, когда вы переходите на летнее время? Разве это «CST» не должно быть более информированным? Согласно комментарию выше, было бы неплохо, если бы мы могли выбрать это на основе времени, которое только что было отформатировано… я учусь ненавидеть часовые пояса.

Ответ №2:

Это работает в Rails 5:

 Time.zone.strptime("2017-05-20 18:20:10", "%Y-%m-%d %H:%M:%S")
 

Возвращает время в указанном часовом поясе.

Ответ №3:

По крайней мере, начиная с Rails 4.2 (возможно, ранее, не тестировался), пока вы используете Time#strptime вместо DateTime.strptime , автоматически применяется текущий часовой пояс:

 > DateTime.strptime('12/28/2016 5:00 PM', '%m/%d/%Y %H:%M %p')
=> Wed, 28 Dec 2016 17:00:00  0000

> Time.strptime('12/28/2016 5:00 PM', '%m/%d/%Y %H:%M %p')
=> 2016-12-28 17:00:00 -0800
 

Комментарии:

1. При этом используется часовой пояс компьютера, а не часовой пояс, установленный в Rails, кстати.

2. @Sean Да, это правда. Обходной путь заключается в использовании Time.zone.strptime для Rails 5 или Time.zone. local_to_utc(DateTime.strptime(‘28.12.2016 5:00 вечера’, ‘%m/%d /%Y %H:%M %p’)) для более старых версий Rails

Ответ №4:

Я «strptiming» эту строку:

 19/05/2014 8:13:26 a.m.
 

Локальная временная метка, которая в моем случае — это Окленд, Новая Зеландия, без отметки часового пояса в строке, как видно.

Насколько я могу судить Time.strptime , в качестве основы используется часовой пояс сервера.

В моей ситуации и в целом хорошей практике мои серверы работают в часовом поясе UTC, поэтому каждый синтаксический анализ строки в конечном итоге создает объект time, время до 0000 (UTC):

 2014-05-19 08:13:26  0000
 

Затем преобразование в in_time_zone(Time.zone) дал:

 Mon, 19 May 2014 20:13:26 NZST  12:00
 

Что, как видно, на 12 часов (смещение UTC) позже, чем фактическое время, которое я хотел.

Я попытался использовать ' Auckland', '...%Z' трюк, как указано выше, без каких-либо изменений. Затем я использовал ' 1200', '...%Z' трюк, описанный выше, который сработал правильно.

Однако я был обеспокоен летним временем, тогда синтаксический анализ будет завершен на час, так что это то, с чем я закончил:

 Time.strptime(call_data[:datetime].gsub(/./, "").gsub(/am/, "AM").gsub(/pm/, "PM")   (Time.zone.now.time_zone.utc_offset/3600).to_s.ljust(4,'0').rjust(6,'  '), '%d/%m/%Y %I:%M:%S %p %Z').in_time_zone(Time.zone)
 

Результат:

 Mon, 19 May 2014 08:13:26 NZST  12:00
 

Это не особенно элегантно, но работает.

Ответ №5:

Вы можете преобразовать дату в часовой пояс с to_time_in_current_zone помощью . Например:

 Date.strptime('04/07/1988', '%m/%d/%Y').to_time_in_current_zone
 

Комментарии:

1. apidock.com/rails/Date/to_time_in_current_zone метод обесценивается извините:(

Ответ №6:

Я боролся с этой же проблемой, пытаясь проанализировать строку даты из формы и сохранить ее независимо от времени сервера. Процесс, которому я следовал, таков. Я заверяю, что часовые пояса RoR и Ruby установлены на UTC. Это упрощает, если сервер также работает по времени UTC.

Я сохраняю языковой стандарт пользователей, используя формат ActiveSupport:TimeZone, например US/Eastern , у меня есть функция интерпретации даты, которая может учитывать строку даты, которая изначально не содержит часовой пояс или смещение

 # date_str looks something like this `12/31/2014 6:22 PM`
# tz is a string containing an ActiveSupport::TimeZone name like `US/Eastern`
def interpret_date(date_str, tz)
  Time.use_zone tz do
    parsed_dt = DateTime.strptime(date_str, '%m/%d/%Y %H:%M %p')
    offset = parsed_dt.in_time_zone(tz).utc_offset
    parsed_dt.advance(seconds: -(offset)).in_time_zone(tz)
  end
end
 

Это, конечно, не кажется идеальным решением, но оно работает.
Шаги для обеспечения правильного часового пояса следующие:

  1. Проанализируйте строку даты в соответствии с нашим форматом
  2. Извлеките текущее смещение utc на основе времени в этом часовом поясе (мы должны сделать это, если дата, которую вы анализируете, находится в dst, в то время как ваше местное время не является dst для восточного времени, это гарантирует, что dst является локальным для анализируемого времени)
  3. Преобразуйте время в нашу целевую локаль и разрешите время сдвигаться с добавлением правильного смещения

Ответ №7:

Для pre Rails 5 будет работать следующее:

 t0 = Time.strptime("2018-05-01", "%Y-%m-%d") # Or whatever
t1 = Time.zone.now.change(year: t0.year, month: t0.month, day: t0.day, hour: t0.hour, min: t0.min, sec: t0.sec, usec: t0.usec)
 

Ответ №8:

Я не могу удалить принятый ответ

Как указал @LeeJarvis в разделе комментариев ниже, Chronic.parse опция не принимается :time_class . Итак, этот ответ содержит ошибки, и, хотя похоже, что он работает, это не так (если только Chronic не разрешает :time_class скоро передавать опцию).)


Хронический драгоценный камень действительно мощный.

Я использую это так:

 Chronic.parse("next 5:00 pm", 
:time_class => ActiveSupport::TimeZone.new(User.first.time_zone)).utc
 

В моем примере выше User.first.time_zone это «Тихоокеанское время (США и Канада)».

Chronic поддерживает множество форматов, поэтому проверьте это на https://github.com/mojombo/chronic

Обязательно передайте :time_class опцию и преобразуйте в UTC (в документации, Хронические наборы :time_class перед вызовом parse . Я избегаю этого подхода, потому что это может привести к тому, что другие части приложения не будут работать)

Документация по часовому поясу находится по адресу http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

Комментарии:

1. Этот ответ неверен. parse() не принимает time_class никакого решения. Он устанавливается Chronic непосредственно на класс. Однако это в конечном итоге изменится в будущих версиях Chronic

2. Мой плохой… Я буду удалять до тех пор, пока эта опция не будет доступна =)

3. Эмм… Я не могу удалить принятый ответ. Я прокомментирую выше.