rails now define dates active ruby-on-rails ruby timezone datetimeoffset strptime

ruby-on-rails - now - ruby time zone



Strptime con Timezone (8)

Tengo una cadena que analizo con DateTime.strptime . La zona horaria de la fecha en la cadena es CET, pero Ruby crea un objeto UTC DateTime que, por supuesto, tiene un desplazamiento de 2 horas.

Actualmente estoy DateTime.strptime().change(:offset => "+0020") el problema con DateTime.strptime().change(:offset => "+0020") pero estoy bastante seguro de que esta no es la forma en que debe funcionar.

¿Puede alguien iluminarme sobre la forma correcta de hacer esto?


Al menos a partir de Rails 4.2 (quizás antes, no lo he probado), siempre que use Time#strptime lugar de DateTime.strptime , la zona horaria actual se aplica automáticamente:

> 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


Esto funciona en Rails 5:

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

Devuelve la hora en la zona horaria especificada.


He estado luchando contra este mismo problema al tratar de analizar una cadena de fecha de un formulario y guardarla independientemente de la hora del servidor. El proceso que he seguido es como tal. Aseguro que las zonas horarias RoR y Ruby están configuradas en UTC. Lo hace más fácil si el servidor también está en la hora UTC.

Estoy almacenando la configuración regional de los usuarios utilizando el formato ActiveSupport: TimeZone como US/Eastern Tengo una función de fecha de interpretación que puede explicar una cadena de fecha que no contiene de forma nativa una zona horaria o un desplazamiento

# 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

Esto, por supuesto, no se siente como una solución perfecta, pero funciona. Los pasos para asegurar la correcta zona horaria son:

  1. Analizar la cadena de fecha de acuerdo a nuestro formato.
  2. Extraiga el desplazamiento utc actual según la hora en esa zona horaria (tenemos que hacerlo si la fecha en la que está analizando está en dst, mientras que su hora local no es dst para la hora del este, esto asegurará que la dst sea local a la analizada hora)
  3. Convierta el tiempo a nuestra configuración regional de destino y permita que cambie el tiempo con el desplazamiento correcto agregado

Lo uso de la siguiente manera:

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)>

¿Es lo que quieres decir?


No puedo borrar una respuesta aceptada

Como @LeeJarvis señaló en la sección de comentarios a continuación, Chronic.parse no acepta una opción :time_class . Así que esta respuesta tiene errores y, aunque parece que funciona, no lo hace (a menos que Chronic permita pasar una opción de :time_class pronto).

La gema crónica es realmente poderosa.

Lo uso así:

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

En mi ejemplo anterior, User.first.time_zone es "Hora del Pacífico (EE. UU. Y Canadá)".

Chronic es compatible con muchos formatos, así que échale un vistazo en https://github.com/mojombo/chronic

Asegúrese de pasar la opción :time_class y de convertir a UTC (en la documentación, Chronic sets :time_class antes de llamar a parse. :time_class este enfoque porque puede hacer que otras partes de la aplicación no funcionen)

La documentación de TimeZone se encuentra en http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html


Para pre Rails 5, funcionará lo siguiente:

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)


Puede convertir la fecha a la zona horaria con to_time_in_current_zone . Por ejemplo:

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


Yo estaba "strptiming" esta cadena:

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

Una marca de tiempo local, que en mi caso es Auckland NZ sin un sello de zona horaria en la cadena como se puede ver.

Lo mejor que puedo decir es que Time.strptime usa la zona horaria del servidor como base.

En mi situación, y una buena práctica general, mis servidores se ejecutan en la zona horaria UTC, por lo que cada análisis de la cadena terminó creando un objeto de tiempo, el tiempo a +0000 (UTC):

2014-05-19 08:13:26 +0000

Luego la conversión a in_time_zone(Time.zone) dio:

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

Lo que se puede ver es 12 horas (la compensación UTC) más tarde de la hora real que quería.

Intenté usar el truco +'' Auckland'', ''...%Z'' como se indica arriba sin ningún cambio. Luego utilicé el + ''+1200'', ''...%Z'' como anteriormente, que funcionó correctamente.

Sin embargo, estaba preocupado por el horario de verano, entonces el análisis se agotaría en una hora, así que esto es lo que he encontrado con:

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)

Resultado:

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

No es particularmente elegante pero funciona.