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


Antworten:


354

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

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

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>

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 ****


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"

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)


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.


3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Chloe
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.