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.