Jak przekonwertować uniksowy znacznik czasu (w sekundach od epoki) na Ruby DateTime?

369

Jak przekonwertować uniksowy znacznik czasu (w sekundach od epoki) na Ruby DateTime?

Tronathan
źródło

Odpowiedzi:

354

DateTime.strptimemoże obsłużyć sekundy od epoki. Liczba musi zostać przekonwertowana na ciąg:

require 'date'
DateTime.strptime("1318996912",'%s')
steenslag
źródło
4
To nie obsługuje ułamków sekund
Dan Sandberg
45
Obsługuje milisekundy z „ %Qtho ” .
Mini John
3
Kontynuacja odpowiedzi @ TheMiniJohn. Wygląda na to, że Timejest to konieczne zamiast DateTime. Skorzystaj, Time.strptime("1318996912345",'%Q').to_fa zobaczysz milisekundy zachowane, a DateTime.strptime("1318996912345",'%Q').to_fnie zachowane.
skensell
625

Przepraszamy, krótka chwila niepowodzenia synapsy. Oto prawdziwa odpowiedź.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Krótki przykład (uwzględnia aktualną strefę czasową systemu):

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

Dalsza aktualizacja (dla 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)>

Ostatnia aktualizacja : Przeprowadziłem testy porównawcze najlepszych rozwiązań w tym wątku, pracując nad usługą HA tydzień lub dwa lata temu, i byłem zaskoczony, że Time.at(..)przewyższa to DateTime.strptime(..)(aktualizacja: dodałem więcej testów porównawczych).

# ~ % 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
źródło
1
Dziękuję ... Poniższa odpowiedź jest nieco bardziej zwięzła, znalazłem Time.at, ale próbowałem znaleźć odpowiednik DateTime.
Tronathan
27
To zabawne, ale Time.at (). To_datetime wydaje się przyjemniejszy niż DateTime.strptime () po prostu ze względu na czytelność ... Przynajmniej dla mnie
tybro0103
34
Nie jest to to samo co powyższy anser, Time.at przyjmuje bieżącą strefę czasową, w której DateTime.strptime używa UTC.
Witalij Babiy
5
Nic dziwnego, że Time.atosiąga lepsze wyniki DateTime.strptime. Ten ostatni musi przeanalizować ciąg znaków, który jest na ogół znacznie wolniejszy niż bezpośrednie pobieranie liczby.
Claw
2
Twój test porównawczy nie jest dokładnie testowany, DateTime.strptimeponieważ tworzy dwa nowe ciągi w każdej iteracji, co jest bardzo drogie. To nie tylko parsowanie łańcucha, jak powiedział
@claw
67

Obsługa stref czasowych

Chcę tylko wyjaśnić, chociaż zostało to skomentowane, aby przyszli ludzie nie przegapili tego bardzo ważnego rozróżnienia.

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

wyświetla wartość zwracaną w UTC i wymaga, aby sekundy były ciągiem, i wysyła obiekt czasu UTC, natomiast

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

wyświetla wartość zwracaną w LOKALNEJ strefie czasowej, zwykle wymaga argumentu FixNum, ale sam obiekt Time wciąż jest w UTC, nawet jeśli wyświetlanie nie jest.

Więc mimo że podałem tę samą liczbę całkowitą do obu metod, najwyraźniej mam dwa różne wyniki ze względu na sposób działania #to_smetody klasy . Jednak, ponieważ @Eero dwukrotnie musiało mi przypominać:

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

Porównanie równości między dwiema zwracanymi wartościami nadal zwraca wartość true. Ponownie #==dzieje się tak, ponieważ wartości są w zasadzie takie same (chociaż różne klasy metoda dba o to za Ciebie), ale #to_smetoda drukuje drastycznie różne ciągi. Chociaż, jeśli spojrzymy na ciągi, zobaczymy, że rzeczywiście są one w tym samym czasie, tylko drukowane w różnych strefach czasowych.

Metoda Wyjaśnienie argumentów

Dokumenty mówią również: „Jeśli podany zostanie argument liczbowy, wynik będzie w czasie lokalnym”. co ma sens, ale było to dla mnie trochę mylące, ponieważ nie podają żadnych przykładów argumentów niecałkowitych w dokumentacji. Tak więc w przypadku niektórych argumentów niecałkowitych:

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

nie możesz użyć argumentu String, ale możesz użyć argumentu Time do, Time.ata zwróci wynik w strefie czasowej argumentu:

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

Benchmarki

Po dyskusji z @AdamEberlin na temat jego odpowiedzi postanowiłem opublikować nieznacznie zmienione testy porównawcze, aby wszystko było jak najbardziej równe. Ponadto, nigdy nie chcę ich ponownie budować, więc jest to tak dobre miejsce, jak każde, aby je uratować.

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

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>

**** edytowane tak, aby nie były całkowicie i całkowicie niepoprawne pod każdym względem ****

**** dodano testy porównawcze ****

WattsInABox
źródło
3
Wydawało się to prawdopodobne, a ja już głosowałem (nie mogę teraz anulować), ale po dalszym sprawdzeniu twojego roszczenia dotyczącego UTC jest nieprawdziwe. Wynikowy obiekt DateTime / Time będzie w UTC vs. lokalny, tak, ale oryginalny znacznik czasu jest interpretowany jako UTC w obu przypadkach! Tak więc moment w czasie jest równy bez względu na metodę. Spróbuj Time.at(1318996912) == DateTime.strptime("1318996912",'%s')w strefie czasowej innej niż UTC, a zobaczysz!
Eero
4
Przykro mi, ale to, co poprawiłeś, jest nadal złe! :-) Uruchom, Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') endaby sprawdzić, czy czasy są równe, nie ma LOKALNEGO znacznika czasu, aw obu przypadkach uniksowy znacznik czasu jest interpretowany jako UTC. Time.at przedstawia wynikowy obiekt Time w lokalnej strefie czasowej i DateTime.strptime przedstawia wynikowy obiekt DateTime w UTC, ale niezależnie od prezentacji są one równe, ponieważ są równoważnym momentem w czasie.
Eero
Oświadczenie, podczas gdy Time.at(1318996912) # => 2011-10-19 00:01:52 -0400wyświetla wartość zwracaną w LOKALNEJ strefie czasowej , nie wydaje się dokładne ... Czy możesz to zweryfikować? Wierzę, że twoje stwierdzenie byłoby prawdziwe tylko wtedy, gdy je użyłeśTime.zone.at(1318996912)
BigRon
Tak, to wydaje się być dokładne. Moja lokalna maszyna jest ustawiona na EST, a godziny są wyświetlane w EST.
WattsInABox
Czy możesz podać przykład, w którym tak nie jest @BigRon? Jaka strefa czasowa, wersja ruby ​​itp. Nie zachowuje się w ten sposób?
WattsInABox
10

Jedno polecenie konwersji daty i godziny na format uniksowy, a następnie na ciąg znaków

    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

w końcu strftime służy do formatowania daty

Przykład:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"
Tejasvi Manmatha
źródło
Należy zauważyć, że format epoki nie ma strefy czasowej, więc łańcuchowanie utc przed łańcuchem do_i nie jest konieczne Time.now.utc.to_i.
Trevor McCasland
1

Informuje o dacie liczby sekund w przyszłości od momentu wykonania kodu.

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

puts (czas)

zgodnie z obecnym czasem odpowiadam na pytanie, które drukuje 047-05-14 05:16:16 +0000(1 miliard sekund w przyszłości)

lub jeśli chcesz liczyć miliardy sekund od określonego czasu, jest to format Time.mktime(year, month,date,hours,minutes)

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

puts („Miałbym 1 miliard sekund na:” + czas)

sbutterworth
źródło
0

Jeśli chcesz tylko datę, możesz zrobić, Date.strptime(invoice.date.to_s, '%s')gdzie invoice.dateprzychodzi w postaci an, Fixnuma następnie przekształcić w a String.

pjammer
źródło
3
Time.at(1500923406).to_date.to_s=>"2017-07-24"
Chloe,