Como converter um carimbo de data / hora unix (segundos desde a época) para Ruby DateTime?


369

Como você converte um carimbo de data / hora Unix (segundos desde a época) para Ruby DateTime?

Respostas:


354

DateTime.strptimepode lidar com segundos desde a época. O número deve ser convertido em uma sequência:

require 'date'
DateTime.strptime("1318996912",'%s')

4
Isso não lida com segundos fracionários
— Dan Sandberg

45
Ele lida com milissegundos com %Qtho.
— Mini John

3
Para acompanhar a resposta de @ TheMiniJohn. Parece que Timeé necessário em vez de DateTime. Portanto, use Time.strptime("1318996912345",'%Q').to_fe você verá os milissegundos preservados, enquanto DateTime.strptime("1318996912345",'%Q').to_fnão o preserva.
— Skinsell

625

Desculpe, breve momento de falha na sinapse. Aqui está a resposta real.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Breve exemplo (isso leva em consideração o fuso horário do sistema atual):

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

Atualização 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)>

Atualização recente : comparei as principais soluções neste segmento enquanto trabalhava em um serviço de alta disponibilidade há uma ou duas semanas e fiquei surpreso ao descobrir que Time.at(..)supera DateTime.strptime(..)(atualização: adicionamos mais benchmarks).

# ~ % 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>

11
Obrigado ... A resposta a seguir é um pouco mais sucinta. Encontrei Time.at, mas estava tentando encontrar um equivalente em DateTime.
— Tronathan 19/10/11

27
É engraçado, mas Time.at () to_datetime parece mais agradável do que DateTime.strptime () simplesmente por causa de legibilidade ... Pelo menos para mim de qualquer maneira.
— tybro0103

34
Não é o mesmo que o anterior, Time.at assume o fuso horário atual, em que DateTime.strptime usa UTC.
— precisa saber é o seguinte

5
Não é de surpreender que Time.atsupere DateTime.strptime. O último tem que analisar uma string, que geralmente é muito mais lenta do que receber um número diretamente.
— Garra

2
Sua referência não é exatamente um teste, DateTime.strptimepois está criando duas novas Strings a cada iteração, o que é muito caro. Não é apenas a seqüência de analisar como @claw disse
— WattsInABox

67

Tratamento de fuso horário

Eu só quero esclarecer, mesmo que isso tenha sido comentado para que futuras pessoas não percam essa distinção muito importante.

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

exibe um valor de retorno em UTC e requer que os segundos sejam uma String e gera um objeto UTC Time, enquanto

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

exibe um valor de retorno no fuso horário LOCAL, normalmente requer um argumento FixNum, mas o próprio objeto Time ainda está no UTC, mesmo que a exibição não esteja.

Portanto, mesmo que eu tenha passado o mesmo número inteiro para os dois métodos, aparentemente tenho dois resultados diferentes por causa de como o #to_smétodo da classe funciona. No entanto, como o @Eero teve que me lembrar duas vezes:

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

Uma comparação de igualdade entre os dois valores de retorno ainda retorna true. Novamente, isso ocorre porque os valores são basicamente os mesmos (embora classes diferentes, o #==método cuide disso para você), mas o #to_smétodo imprime cadeias drasticamente diferentes. Embora, se olharmos para as strings, podemos ver que elas são de fato a mesma hora, apenas impressas em fusos horários diferentes.

Método Argumento Esclarecimento

Os documentos também dizem "Se um argumento numérico for fornecido, o resultado será no horário local". o que faz sentido, mas foi um pouco confuso para mim porque eles não dão exemplos de argumentos não inteiros nos documentos. Portanto, para alguns exemplos de argumentos não inteiros:

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

você não pode usar um argumento String, mas pode usar um argumento Time Time.ate ele retornará o resultado no fuso horário do argumento:

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

Benchmarks

Após uma discussão com o @AdamEberlin sobre sua resposta, decidi publicar benchmarks ligeiramente alterados para tornar tudo o mais igual possível. Além disso, nunca quero ter que construí-las novamente, para que este seja o melhor lugar para salvá-las.

Time.at (int) .to_datetime ~ 2.8x mais 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 não ser completamente e totalmente incorreto em todos os sentidos ****

**** benchmarks adicionados ****


3
Parecia plausível, e eu já votei (não é possível rescindir agora), mas após uma verificação mais aprofundada, sua reivindicação em relação à UTC é falsa. O objeto DateTime / Time resultante estará no UTC versus local, sim, mas o carimbo de data / hora original é interpretado como estando no UTC nos dois casos! Portanto, o momento é igual, independentemente do método. Experimente Time.at(1318996912) == DateTime.strptime("1318996912",'%s')em um fuso horário não UTC e você verá!
— Eero

4
Sinto muito, mas o que você corrigiu ainda está errado! :-) Corra Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endpara verificar se os horários são iguais, se não existe um registro de data e hora LOCAL e, em ambos os casos, o registro de data e hora do Unix é interpretado como estando no UTC. Time.at apresenta o objeto Time resultante no fuso horário local e DateTime.strptime apresenta o objeto DateTime resultante no UTC, mas independentemente da apresentação, eles são iguais, pois são o momento equivalente no tempo.
— Eero

A declaração que Time.at(1318996912) # => 2011-10-19 00:01:52 -0400exibe um valor de retorno no fuso horário LOCAL não parece ser precisa ... Você pode verificar? Acredito que sua declaração só seria verdade se você usouTime.zone.at(1318996912)
— BigRon

Sim, isso parece ser preciso. Minha máquina local está definida como EST e os horários são exibidos em EST.
— precisa saber é o seguinte

Você pode fornecer um exemplo em que esse não é o caso @BigRon? Qual fuso horário, versão ruby, etc, não se comporta dessa maneira?
— precisa saber é o seguinte

10

Um comando para converter data e hora no formato Unix e depois em string

    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 é usado para formatar a data

Exemplo:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"

Uma coisa a observar é que o formato da época não possui um fuso horário, portanto, encadear utc antes de encadear to_i não é necessário Time.now.utc.to_i.
— Trevor McCasland 31/01

1

Isso informa a data do número de segundos no futuro a partir do momento em que você executa o código.

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

põe (hora)

de acordo com o horário atual, estou respondendo à pergunta impressa 047-05-14 05:16:16 +0000(1 bilhão de segundos no futuro)

ou se você quiser contar bilhões de segundos de um horário específico, está no formato Time.mktime(year, month,date,hours,minutes)

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

puts ("Eu teria 1 bilhão de segundos em:" + hora)


0

Se você queria apenas uma Data, pode fazer de Date.strptime(invoice.date.to_s, '%s')onde invoice.datevem na forma de um Fixnume depois convertido em um String.


3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
— Chloe
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.