¿Cómo convertir una marca de tiempo de Unix (segundos desde la época) a Ruby DateTime?

369

¿Cómo se convierte una marca de tiempo de Unix (segundos desde la época) a Ruby DateTime?

Tronathan
fuente

Respuestas:

354

DateTime.strptimepuede manejar segundos desde la época. El número debe convertirse en una cadena:

require 'date'
DateTime.strptime("1318996912",'%s')
steenslag
fuente
44
Esto no maneja segundos fraccionarios
Dan Sandberg
45
Se maneja milisegundos con ' %Qtho.
Mini John
3
Para seguir la respuesta de @ TheMiniJohn. Parece que Timees necesario en lugar de DateTime. Así que use Time.strptime("1318996912345",'%Q').to_fy verá los milisegundos preservados, mientras DateTime.strptime("1318996912345",'%Q').to_fque no los conserva.
skensell
625

Lo siento, breve momento de falla de sinapsis. Aquí está la respuesta real.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Breve ejemplo (esto tiene en cuenta la zona horaria actual del sistema):

$ date +%s
1318996912

$ irb

ruby-1.9.2-p180 :001 > require 'date'
 => true 

ruby-1.9.2-p180 :002 > Time.at(1318996912).to_datetime
 => #<DateTime: 2011-10-18T23:01:52-05:00 (13261609807/5400,-5/24,2299161)> 

Actualización adicional (para UTC):

ruby-1.9.2-p180 :003 > Time.at(1318996912).utc.to_datetime
 => #<DateTime: 2011-10-19T04:01:52+00:00 (13261609807/5400,0/1,2299161)>

Actualización reciente : hice una evaluación comparativa de las mejores soluciones en este hilo mientras trabajaba en un servicio de alta disponibilidad hace una semana o dos, y me sorprendió encontrar que tiene un Time.at(..)rendimiento superior DateTime.strptime(..)(actualización: agregó más puntos de referencia).

# ~ % ruby -v
#  => ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin13.0]

irb(main):038:0> Benchmark.measure do
irb(main):039:1*   ["1318996912", "1318496912"].each do |s|
irb(main):040:2*     DateTime.strptime(s, '%s')
irb(main):041:2>   end
irb(main):042:1> end

=> #<Benchmark ... @real=2.9e-05 ... @total=0.0>

irb(main):044:0> Benchmark.measure do
irb(main):045:1>   [1318996912, 1318496912].each do |i|
irb(main):046:2>     DateTime.strptime(i.to_s, '%s')
irb(main):047:2>   end
irb(main):048:1> end

=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>

irb(main):050:0* Benchmark.measure do
irb(main):051:1*   ["1318996912", "1318496912"].each do |s|
irb(main):052:2*     Time.at(s.to_i).to_datetime
irb(main):053:2>   end
irb(main):054:1> end

=> #<Benchmark ... @real=1.5e-05 ... @total=0.0>

irb(main):056:0* Benchmark.measure do
irb(main):057:1*   [1318996912, 1318496912].each do |i|
irb(main):058:2*     Time.at(i).to_datetime
irb(main):059:2>   end
irb(main):060:1> end

=> #<Benchmark ... @real=2.0e-05 ... @total=0.0>
Adam Eberlin
fuente
1
Gracias ... La siguiente respuesta es un poco más sucinta, encontré Time.at pero estaba tratando de encontrar un equivalente de DateTime.
Tronathan
27
Es divertido, pero Time.at (). To_datetime parece más agradable que DateTime.strptime () simplemente debido a la legibilidad ... Al menos para mí de todos modos
tybro0103
34
Esto no es lo mismo que la respuesta anterior, Time.at asume la zona horaria actual, donde DateTime.strptime usa UTC.
Vitaly Babiy
55
No es demasiado sorprendente que Time.attenga un rendimiento superior DateTime.strptime. Este último tiene que analizar una cadena, que generalmente es mucho más lenta que tomar un número directamente.
Garra
2
Su punto de referencia no está probando exactamente DateTime.strptimeporque está creando dos nuevas cadenas cada iteración, lo cual es muy costoso. No es solo el análisis de la cadena como dijo
@claw
67

Manejo de zona horaria

Solo quiero aclarar, a pesar de que esto se ha comentado para que las personas futuras no se pierdan esta distinción muy importante.

DateTime.strptime("1318996912",'%s') # => Wed, 19 Oct 2011 04:01:52 +0000

muestra un valor de retorno en UTC y requiere que los segundos sean una cadena y genera un objeto de hora UTC, mientras que

Time.at(1318996912) # => 2011-10-19 00:01:52 -0400

muestra un valor de retorno en el huso horario LOCAL, normalmente requiere un argumento FixNum, pero el objeto Tiempo en sí todavía está en UTC aunque la pantalla no lo esté.

Entonces, aunque pasé el mismo número entero a ambos métodos, aparentemente obtuve dos resultados diferentes debido a cómo funciona el #to_smétodo de la clase . Sin embargo, como @Eero tuvo que recordarme dos veces:

Time.at(1318996912) == DateTime.strptime("1318996912",'%s') # => true

Una comparación de igualdad entre los dos valores devueltos aún devuelve verdadero. Nuevamente, esto se debe a que los valores son básicamente los mismos (aunque son diferentes clases, el #==método se encarga de esto por usted), pero el #to_smétodo imprime cadenas drásticamente diferentes. Aunque, si miramos las cadenas, podemos ver que en realidad son la misma hora, solo impresas en diferentes zonas horarias.

Método Argumento Aclaración

Los documentos también dicen "Si se proporciona un argumento numérico, el resultado es en hora local". lo cual tiene sentido, pero fue un poco confuso para mí porque no dan ningún ejemplo de argumentos no enteros en los documentos. Entonces, para algunos ejemplos de argumentos no enteros:

Time.at("1318996912")
TypeError: can't convert String into an exact number

no puede usar un argumento String, pero puede usar un argumento Time en Time.aty devolverá el resultado en la zona horaria del argumento:

Time.at(Time.new(2007,11,1,15,25,0, "+09:00"))
=> 2007-11-01 15:25:00 +0900

Puntos de referencia

Después de una discusión con @AdamEberlin sobre su respuesta, decidí publicar puntos de referencia ligeramente modificados para hacer que todo sea lo más igual posible. Además, nunca quiero tener que volver a construirlos, así que este es un lugar tan bueno como cualquier otro para salvarlos.

Time.at (int) .to_datetime ~ 2.8x más rápido

09:10:58-watsw018:~$ ruby -v
ruby 2.3.7p456 (2018-03-28 revision 63024) [universal.x86_64-darwin18]
09:11:00-watsw018:~$ irb
irb(main):001:0> require 'benchmark'
=> true
irb(main):002:0> require 'date'
=> true
irb(main):003:0>
irb(main):004:0* format = '%s'
=> "%s"
irb(main):005:0> times = ['1318996912', '1318496913']
=> ["1318996912", "1318496913"]
irb(main):006:0> int_times = times.map(&:to_i)
=> [1318996912, 1318496913]
irb(main):007:0>
irb(main):008:0* datetime_from_strptime = DateTime.strptime(times.first, format)
=> #<DateTime: 2011-10-19T04:01:52+00:00 ((2455854j,14512s,0n),+0s,2299161j)>
irb(main):009:0> datetime_from_time = Time.at(int_times.first).to_datetime
=> #<DateTime: 2011-10-19T00:01:52-04:00 ((2455854j,14512s,0n),-14400s,2299161j)>
irb(main):010:0>
irb(main):011:0* datetime_from_strptime === datetime_from_time
=> true
irb(main):012:0>
irb(main):013:0* Benchmark.measure do
irb(main):014:1*   100_000.times {
irb(main):015:2*     times.each do |i|
irb(main):016:3*       DateTime.strptime(i, format)
irb(main):017:3>     end
irb(main):018:2>   }
irb(main):019:1> end
=> #<Benchmark::Tms:0x00007fbdc18f0d28 @label="", @real=0.8680500000045868, @cstime=0.0, @cutime=0.0, @stime=0.009999999999999998, @utime=0.86, @total=0.87>
irb(main):020:0>
irb(main):021:0* Benchmark.measure do
irb(main):022:1*   100_000.times {
irb(main):023:2*     int_times.each do |i|
irb(main):024:3*       Time.at(i).to_datetime
irb(main):025:3>     end
irb(main):026:2>   }
irb(main):027:1> end
=> #<Benchmark::Tms:0x00007fbdc3108be0 @label="", @real=0.33059399999910966, @cstime=0.0, @cutime=0.0, @stime=0.0, @utime=0.32000000000000006, @total=0.32000000000000006>

**** editado para no ser completamente y totalmente incorrecto en todos los sentidos ****

**** puntos de referencia agregados ****

WattsInABox
fuente
3
Parecía plausible, y ya voté (no puedo rescindirlo ahora), pero después de verificar su reclamo con respecto a UTC no es cierto. El objeto DateTime / Time resultante estará en UTC vs local, sí, ¡pero la marca de tiempo original se interpreta como UTC en ambos casos! Entonces el momento en el tiempo es igual independientemente del método. ¡Pruébelo Time.at(1318996912) == DateTime.strptime("1318996912",'%s')en una zona horaria que no sea UTC y verá!
Eero
44
Lo siento, ¡pero lo que corrigiste todavía está mal! :-) Ejecute Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endpara verificar que los tiempos son iguales, no hay marca de tiempo LOCAL, y en ambos casos la marca de tiempo de Unix se interpreta como en UTC. Time.at presenta el objeto de tiempo que resulta en la zona horaria local, y DateTime.strptime presenta el objeto DateTime que resulta en UTC, pero independientemente de presentación son iguales, ya que son el equivalente momento en el tiempo.
Eero
La declaración mientras Time.at(1318996912) # => 2011-10-19 00:01:52 -0400muestra un valor de retorno en la zona horaria LOCAL no parece ser precisa ... ¿Puede verificar? Creo que su declaración solo sería cierta si la usaraTime.zone.at(1318996912)
BigRon
Sí, eso parece ser exacto. Mi máquina local está configurada en EST y las horas se muestran en EST.
WattsInABox
¿Puede proporcionar un ejemplo donde este no sea el caso @BigRon? ¿Qué zona horaria, versión ruby, etc. no se comporta de esta manera?
WattsInABox
10

Un comando para convertir la fecha y hora al formato Unix y luego a una cadena

    DateTime.strptime(Time.now.utc.to_i.to_s,'%s').strftime("%d %m %y")

    Time.now.utc.to_i #Converts time from Unix format
    DateTime.strptime(Time.now.utc.to_i.to_s,'%s') #Converts date and time from unix format to DateTime

finalmente strftime se usa para formatear la fecha

Ejemplo:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"
Tejasvi Manmatha
fuente
Una cosa a tener en cuenta es que el formato de época no tiene una zona horaria, por lo que no es necesario encadenar utc antes de encadenar to_i Time.now.utc.to_i.
Trevor McCasland
1

Esto le indica la fecha del número de segundos en el futuro desde el momento en que ejecuta el código.

time = Time.new + 1000000000 #date in 1 billion seconds

pone (tiempo)

De acuerdo con la hora actual, estoy respondiendo la pregunta que imprime 047-05-14 05:16:16 +0000(mil millones de segundos en el futuro)

o si quieres contar mil millones de segundos de un momento en particular, está en formato Time.mktime(year, month,date,hours,minutes)

time = Time.mktime(1987,8,18,6,45) + 1000000000

pone ("Sería 1 mil millones de segundos en:" + tiempo)

sbutterworth
fuente
0

Si solo quería una Fecha, puede hacer Date.strptime(invoice.date.to_s, '%s')donde invoice.dateviene en forma de una Fixnumy luego convertirla en una String.

pjammer
fuente
3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Chloe