Come convertire un timestamp unix (secondi dall'epoca) in Ruby DateTime?


Risposte:


354

DateTime.strptimepuò gestire i secondi dall'epoca. Il numero deve essere convertito in una stringa:

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

4
Questo non gestisce i secondi frazionari
— Dan Sandberg,

45
Gestisce i millisecondi con ' %Qtho.
— Mini John,

3
Per dare seguito alla risposta di @ TheMiniJohn. Sembra che Timesia necessario invece di DateTime. Quindi usa Time.strptime("1318996912345",'%Q').to_fe vedrai i millisecondi preservati, mentre DateTime.strptime("1318996912345",'%Q').to_fnon li conserva.
— skensell,

625

Siamo spiacenti, breve momento del fallimento della sinapsi. Ecco la vera risposta.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Breve esempio (questo tiene conto dell'attuale fuso orario 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)> 

Ulteriore aggiornamento (per 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)>

Aggiornamento recente : ho confrontato le migliori soluzioni in questo thread mentre lavoravo su un servizio HA una o due settimane fa e sono stato sorpreso di scoprire che Time.at(..)supera le prestazioni DateTime.strptime(..)(aggiornamento: aggiunto più benchmark).

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

1
Grazie ... La seguente risposta è un po 'più concisa, ho trovato Time.at ma stavo cercando di trovare un equivalente DateTime.
— Tronathan,

27
È divertente ma Time.at (). To_datetime sembra più piacevole di DateTime.strptime () semplicemente per la leggibilità ... Almeno per me comunque
— tybro0103

34
Questo non è lo stesso della risposta precedente, Time.at assume il fuso orario corrente, dove DateTime.strptime utilizza UTC.
— Vitaly Babiy,

5
Non è troppo sorprendente che Time.atsuperi DateTime.strptime. Quest'ultimo deve analizzare una stringa, che è generalmente molto più lenta rispetto al prendere direttamente un numero.
— Artiglio

2
Il tuo benchmark non sta esattamente testando DateTime.strptimeperché sta creando due nuove stringhe ogni iterazione che è molto costoso. Non è solo la stringa che analizza come ha detto
— @claw

67

Gestione del fuso orario

Voglio solo chiarire, anche se questo è stato commentato in modo che le persone future non perdano questa distinzione molto importante.

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

visualizza un valore di ritorno in UTC e richiede che i secondi siano una stringa e genera un oggetto Time UTC, mentre

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

visualizza un valore di ritorno nel fuso orario LOCAL, normalmente richiede un argomento FixNum, ma l'oggetto Time stesso è ancora in UTC anche se il display non lo è.

Quindi, anche se ho passato lo stesso numero intero a entrambi i metodi, apparentemente ho ottenuto due risultati diversi a causa di come funziona il #to_smetodo della classe . Tuttavia, poiché @Eero ha dovuto ricordarmi due volte di:

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

Un confronto di uguaglianza tra i due valori restituiti restituisce comunque true. Ancora una volta, questo perché i valori sono sostanzialmente gli stessi (anche se classi diverse, il #==metodo si occupa di questo per te), ma il #to_smetodo stampa stringhe drasticamente diverse. Sebbene, se osserviamo le stringhe, possiamo vedere che sono effettivamente allo stesso tempo, appena stampate in diversi fusi orari.

Chiarimento dell'argomento del metodo

I documenti dicono anche "Se viene fornito un argomento numerico, il risultato è in ora locale". il che ha senso, ma è stato un po 'confuso per me perché non forniscono alcun esempio di argomenti non interi nei documenti. Quindi, per alcuni esempi di argomenti non interi:

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

non puoi usare un argomento String, ma puoi usare un argomento Time in Time.ate restituirà il risultato nel fuso orario dell'argomento:

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

Punti di riferimenti

Dopo una discussione con @AdamEberlin sulla sua risposta, ho deciso di pubblicare benchmark leggermente modificati per rendere tutto il più uguale possibile. Inoltre, non voglio mai doverli ricostruire di nuovo, quindi questo è un posto buono come un altro per salvarli.

Time.at (int) .to_datetime ~ 2.8x più veloce

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>

**** modificato per non essere completamente e totalmente errato in ogni modo ****

**** benchmark aggiunti ****


3
Sembra plausibile, e ho già effettuato l'upgrade (non posso revocare ora), ma dopo aver verificato ulteriormente il tuo reclamo in merito a UTC è falso. L'oggetto DateTime / Time risultante sarà in UTC vs locale, sì, ma il timestamp originale viene interpretato come in UTC in entrambi i casi! Quindi il momento nel tempo è uguale indipendentemente dal metodo. Prova Time.at(1318996912) == DateTime.strptime("1318996912",'%s')in un fuso orario non UTC e vedrai!
— Eero,

4
Mi dispiace, ma quello che hai corretto è ancora sbagliato! :-) Esegui Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endper verificare che i tempi siano uguali, non esiste un timestamp LOCALE e in entrambi i casi il timestamp Unix viene interpretato come in UTC. Time.at presenta l'oggetto Time risultante nel fuso orario locale e DateTime.strptime presenta l'oggetto DateTime risultante in UTC, ma indipendentemente dalla presentazione sono uguali, poiché sono il momento equivalente nel tempo.
— Eero,

L'istruzione considerando che Time.at(1318996912) # => 2011-10-19 00:01:52 -0400visualizza un valore di ritorno nel fuso orario LOCALE non sembra essere preciso ... Puoi verificarlo? Credo che la tua affermazione sarebbe vera solo se avessi usatoTime.zone.at(1318996912)
— BigRon il

Sì, sembra essere accurato. La mia macchina locale è impostata su EST e i tempi sono visualizzati su EST.
— WattsInABox,

Puoi fornire un esempio in cui non è così @BigRon? Quale fuso orario, versione rubino, ecc. Non si comporta in questo modo?
— WattsInABox,

10

Un comando per convertire l'ora della data in formato Unix e quindi in stringa

    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

infine strftime viene utilizzato per formattare la data

Esempio:

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

Una cosa da notare è che il formato dell'epoca non ha un fuso orario, quindi non è necessario concatenare utc prima del concatenamento a_i Time.now.utc.to_i.
— Trevor McCasland,

1

Questo ti dice la data del numero di secondi in futuro dal momento in cui esegui il codice.

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

puts (tempo)

in base all'ora corrente sto rispondendo alla domanda che stampa 047-05-14 05:16:16 +0000(1 miliardo di secondi in futuro)

o se vuoi contare miliardi di secondi da un determinato momento, è in formato Time.mktime(year, month,date,hours,minutes)

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

mette ("Avrei 1 miliardo di secondi dopo:" + tempo)


0

Se volevi solo una data, puoi fare Date.strptime(invoice.date.to_s, '%s')dove invoice.datearriva sotto forma di un Fixnume quindi convertito in un String.


3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
— Chloe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.