Wie konvertiere ich einen Unix-Zeitstempel (Sekunden seit der Epoche) in Ruby DateTime?

369

Wie konvertiert man einen Unix-Zeitstempel (Sekunden seit der Epoche) in Ruby DateTime?

Tronathan
quelle

Antworten:

354

DateTime.strptimekann Sekunden seit der Epoche verarbeiten. Die Nummer muss in eine Zeichenfolge konvertiert werden:

require 'date'
DateTime.strptime("1318996912",'%s')
steenslag
quelle
4
Dies behandelt keine Sekundenbruchteile
Dan Sandberg
45
Es verarbeitet Milisekunden mit ' %Qtho.
Mini John
3
Um die Antwort von @ TheMiniJohn zu verfolgen. Es sieht so aus, als wäre Timees notwendig statt DateTime. Verwenden Time.strptime("1318996912345",'%Q').to_fSie also und Sie werden sehen, dass die Millisekunden erhalten bleiben, während DateTime.strptime("1318996912345",'%Q').to_fsie nicht erhalten bleiben.
Skensell
625

Entschuldigung, kurzer Moment des Synapsenversagens. Hier ist die wahre Antwort.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Kurzes Beispiel (dies berücksichtigt die aktuelle Systemzeitzone):

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

Weiteres Update (für 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)>

Letztes Update : Ich habe die Top-Lösungen in diesem Thread vor ein oder zwei Wochen bei der Arbeit an einem HA-Service verglichen und war überrascht, dass diese Time.at(..)Outperformance erzielt wurde DateTime.strptime(..)(Update: Weitere Benchmarks hinzugefügt).

# ~ % 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
quelle
1
Vielen Dank ... Die folgende Antwort ist etwas prägnanter. Ich habe Time.at gefunden, aber versucht, ein DateTime-Äquivalent zu finden.
Tronathan
27
Es ist lustig, aber Time.at (). To_datetime scheint angenehmer als DateTime.strptime (), einfach wegen der Lesbarkeit ... Zumindest für mich sowieso
tybro0103
34
Dies ist nicht dasselbe wie der obige Anser. Time.at nimmt die aktuelle Zeitzone an, in der DateTime.strptime UTC verwendet.
Vitaly Babiy
5
Es ist nicht allzu überraschend, dass es Time.atbesser abschneidet DateTime.strptime. Letzterer muss eine Zeichenfolge analysieren, die im Allgemeinen viel langsamer ist als die direkte Eingabe einer Zahl.
Klaue
2
Ihr Benchmark wird nicht genau getestet, DateTime.strptimeda bei jeder Iteration zwei neue Strings erstellt werden, was sehr teuer ist. Es ist nicht nur das Parsen von Strings, wie @claw sagte
WattsInABox
67

Zeitzonenbehandlung

Ich möchte nur klarstellen, obwohl dies kommentiert wurde, damit zukünftige Leute diese sehr wichtige Unterscheidung nicht verpassen.

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

Zeigt einen Rückgabewert in UTC an und erfordert, dass die Sekunden ein String sind, und gibt ein UTC-Zeitobjekt aus

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

Zeigt einen Rückgabewert in der LOCAL-Zeitzone an, erfordert normalerweise ein FixNum-Argument, aber das Time-Objekt selbst befindet sich immer noch in UTC, obwohl die Anzeige dies nicht ist.

Obwohl ich beiden Methoden dieselbe Ganzzahl übergeben habe, habe ich aufgrund der Funktionsweise der Klassenmethode anscheinend zwei unterschiedliche Ergebnisse erzielt #to_s. Da @Eero mich jedoch zweimal daran erinnern musste:

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

Ein Gleichheitsvergleich zwischen den beiden Rückgabewerten gibt weiterhin true zurück. Dies liegt wiederum daran, dass die Werte im Grunde genommen gleich sind (obwohl verschiedene Klassen, die #==Methode kümmert sich für Sie darum), aber die #to_sMethode druckt drastisch unterschiedliche Zeichenfolgen. Wenn wir uns die Zeichenfolgen ansehen, können wir jedoch feststellen, dass sie tatsächlich dieselbe Zeit sind und nur in verschiedenen Zeitzonen gedruckt werden.

Klärung des Methodenarguments

In den Dokumenten heißt es außerdem: "Wenn ein numerisches Argument angegeben wird, ist das Ergebnis in Ortszeit." Das macht Sinn, war aber für mich etwas verwirrend, weil sie in den Dokumenten keine Beispiele für nicht ganzzahlige Argumente enthalten. Für einige Beispiele für nicht ganzzahlige Argumente:

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

Sie können kein String-Argument verwenden, aber Sie können ein Time-Argument in verwenden, Time.atund das Ergebnis wird in der Zeitzone des Arguments zurückgegeben:

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

Benchmarks

Nach einer Diskussion mit @AdamEberlin über seine Antwort habe ich beschlossen, leicht geänderte Benchmarks zu veröffentlichen, um alles so gleich wie möglich zu machen. Außerdem möchte ich diese nie wieder bauen müssen, damit dies so gut wie jeder andere Ort ist, um sie zu retten.

Time.at (int) .to_datetime ~ 2.8x schneller

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>

**** bearbeitet, um nicht in jeder Hinsicht vollständig und völlig falsch zu sein ****

**** Benchmarks hinzugefügt ****

WattsInABox
quelle
3
Schien plausibel, und ich habe bereits zugestimmt (kann jetzt nicht zurücktreten), aber nach weiterer Prüfung ist Ihre Behauptung bezüglich UTC nicht wahr. Das resultierende DateTime / Time-Objekt befindet sich in UTC im Vergleich zu local, ja, aber der ursprüngliche Zeitstempel wird in beiden Fällen als in UTC interpretiert! Der Zeitpunkt ist also unabhängig von der Methode gleich. Versuchen Sie es Time.at(1318996912) == DateTime.strptime("1318996912",'%s')in einer Nicht-UTC-Zeitzone und Sie werden sehen!
Eero
4
Es tut mir leid, aber was Sie korrigiert haben, ist immer noch falsch! :-) Führen Sie diese Option aus Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') end, um sicherzustellen , dass die Zeiten gleich sind. Es gibt keinen lokalen Zeitstempel. In beiden Fällen wird der Unix-Zeitstempel als UTC interpretiert. Time.at präsentiert das resultierende Zeitobjekt in der lokalen Zeitzone, und DateTime.strptime präsentiert das resultierende Datetime - Objekt in UTC, aber unabhängig davon , Präsentation sie gleich sind, da sie der entsprechende Zeitpunkt ist.
Eero
Die Aussage, während Time.at(1318996912) # => 2011-10-19 00:01:52 -0400ein Rückgabewert in der lokalen Zeitzone angezeigt wird, scheint nicht korrekt zu sein ... Können Sie dies bitte überprüfen? Ich glaube, Ihre Aussage wäre nur wahr, wenn SieTime.zone.at(1318996912)
BigRon
Ja, das scheint genau zu sein. Mein lokaler Computer ist auf EST eingestellt und die Zeiten werden in EST angezeigt.
WattsInABox
Können Sie ein Beispiel nennen, bei dem dies bei BigRon nicht der Fall ist? Welche Zeitzone, Ruby-Version usw. verhält sich nicht so?
WattsInABox
10

Ein Befehl zum Konvertieren der Datums- und Uhrzeit in das Unix-Format und anschließend in eine Zeichenfolge

    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

Schließlich wird strftime zum Formatieren des Datums verwendet

Beispiel:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"
Tejasvi Manmatha
quelle
Zu beachten ist, dass das Epochenformat keine Zeitzone hat, sodass eine Verkettung von utc vor der Verkettung mit_i in nicht erforderlich ist Time.now.utc.to_i.
Trevor McCasland
1

Hier erfahren Sie das Datum der Anzahl der Sekunden in der Zukunft ab dem Zeitpunkt, an dem Sie den Code ausführen.

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

setzt (Zeit)

Nach der aktuellen Zeit beantworte ich die Frage, die gedruckt wird 047-05-14 05:16:16 +0000(1 Milliarde Sekunden in der Zukunft).

oder wenn Sie Milliarden Sekunden ab einer bestimmten Zeit zählen möchten, ist es im Format Time.mktime(year, month,date,hours,minutes)

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

setzt ("Ich wäre 1 Milliarde Sekunden alt auf:" + Zeit)

sbutterworth
quelle
0

Wenn Sie nur ein Datum möchten, können Sie tun, Date.strptime(invoice.date.to_s, '%s')wo es invoice.datein Form eines Fixnumund dann in ein konvertiert wird String.

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