Bagaimana cara mengubah cap waktu unix (detik sejak zaman) ke Ruby DateTime?

369

Bagaimana Anda mengonversi cap waktu Unix (detik sejak zaman) ke Ruby DateTime?

Tronathan
sumber

Jawaban:

354

DateTime.strptimedapat menangani detik sejak zaman. Jumlahnya harus dikonversi ke string:

require 'date'
DateTime.strptime("1318996912",'%s')
steenslag
sumber
4
Ini tidak menangani detik pecahan
Dan Sandberg
45
Itu menangani milidetik dengan ' %Qtho.
Mini John
3
Untuk menindaklanjuti jawaban @ TheMiniJohn. Sepertinya Timediperlukan sebagai ganti DateTime. Jadi gunakan Time.strptime("1318996912345",'%Q').to_fdan Anda akan melihat milidetik dipertahankan, sementara DateTime.strptime("1318996912345",'%Q').to_ftidak melestarikannya.
skensell
625

Maaf, saat singkat kegagalan sinaps. Inilah jawaban sebenarnya.

require 'date'

Time.at(seconds_since_epoch_integer).to_datetime

Contoh singkat (ini memperhitungkan zona waktu sistem saat ini):

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

Pembaruan lebih lanjut (untuk 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)>

Pembaruan Terbaru : Saya membuat benchmark solusi teratas di utas ini saat mengerjakan layanan HA satu atau dua minggu yang lalu, dan terkejut menemukan itu Time.at(..)mengungguli DateTime.strptime(..)(pembaruan: menambahkan lebih banyak tolok ukur).

# ~ % 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
sumber
1
Terima kasih ... Jawaban berikut ini sedikit lebih ringkas, saya menemukan Time.at tetapi sedang mencoba mencari yang setara dengan DateTime.
Tronathan
27
Ini lucu tapi Time.at (). To_datetime tampaknya lebih menyenangkan daripada DateTime.strptime () hanya karena mudah dibaca ... Setidaknya bagi saya
tybro0103
34
Ini tidak sama dengan server di atas, Time.at mengasumsikan zona waktu saat ini, di mana DateTime.strptime menggunakan UTC.
Vitaly Babiy
5
Ini tidak terlalu mengejutkan bahwa Time.atmelebihi DateTime.strptime. Yang terakhir harus mengurai string, yang umumnya jauh lebih lambat daripada mengambil nomor secara langsung.
Claw
2
Benchmark Anda tidak benar-benar menguji DateTime.strptimekarena itu menciptakan dua String baru setiap iterasi yang sangat mahal. Ini bukan hanya penguraian string seperti @claw berkata
WattsInABox
67

Penanganan Zona Waktu

Saya hanya ingin mengklarifikasi, meskipun ini telah dikomentari sehingga orang-orang masa depan tidak ketinggalan perbedaan yang sangat penting ini.

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

menampilkan nilai kembali dalam UTC dan membutuhkan detik untuk menjadi String dan output objek Waktu UTC, sedangkan

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

menampilkan nilai kembali di zona waktu LOCAL, biasanya memerlukan argumen FixNum, tetapi objek Waktu itu sendiri masih dalam UTC meskipun tampilan tidak.

Jadi meskipun saya melewati bilangan bulat yang sama untuk kedua metode, saya tampaknya dua hasil yang berbeda karena cara kerja kelas #to_s. Namun, karena @Eero harus mengingatkan saya dua kali tentang:

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

Perbandingan kesetaraan antara dua nilai kembali masih mengembalikan benar. Sekali lagi, ini karena nilainya pada dasarnya sama (walaupun kelas berbeda, #==metode ini menangani Anda), tetapi #to_smetode ini mencetak string yang berbeda secara drastis. Meskipun, jika kita melihat senarnya, kita dapat melihat mereka memang memiliki waktu yang sama, hanya dicetak di zona waktu yang berbeda.

Metode Klarifikasi Argumen

Dokumen juga mengatakan "Jika argumen numerik diberikan, hasilnya adalah waktu setempat." yang masuk akal, tetapi sedikit membingungkan bagi saya karena mereka tidak memberikan contoh argumen non-integer dalam dokumen. Jadi, untuk beberapa contoh argumen non-integer:

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

Anda tidak bisa menggunakan argumen String, tetapi Anda bisa menggunakan argumen Time Time.atdan itu akan mengembalikan hasilnya di zona waktu argumen:

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

Tolak ukur

Setelah berdiskusi dengan @AdamEberlin tentang jawabannya, saya memutuskan untuk menerbitkan tolok ukur yang sedikit berubah untuk membuat semuanya sederajat mungkin. Juga, saya tidak pernah ingin harus membangun ini lagi jadi ini adalah tempat yang baik untuk menyelamatkan mereka.

Time.at (int) .to_datetime ~ 2.8x lebih cepat

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>

**** diedit agar tidak sepenuhnya dan sepenuhnya salah dalam segala hal ****

**** menambahkan tolok ukur ****

WattsInABox
sumber
3
Tampaknya masuk akal, dan saya sudah membatalkan (tidak bisa membatalkan sekarang), tetapi setelah memeriksa lebih lanjut klaim Anda tentang UTC tidak benar. Objek DateTime / Waktu yang dihasilkan akan dalam UTC vs lokal, ya, tapi cap waktu asli ditafsirkan sebagai dalam UTC dalam kedua kasus! Jadi momen dalam waktu adalah sama terlepas dari metode. Coba Time.at(1318996912) == DateTime.strptime("1318996912",'%s')di zona waktu non-UTC dan Anda akan melihat!
Eero
4
Maaf, tetapi apa yang Anda koreksi masih salah! :-) Jalankan Time.use_zone "Samoa" do Time.at(1318996912) == DateTime.strptime("1318996912",'%s') enduntuk memverifikasi bahwa waktunya sama, tidak ada cap waktu LOCAL, dan dalam kedua kasus cap waktu Unix ditafsirkan sebagai dalam UTC. Time.at menyajikan objek Waktu yang dihasilkan di zona waktu lokal, dan DateTime.strptime menyajikan objek DateTime yang dihasilkan di UTC, tetapi terlepas dari presentasi, mereka sama, karena mereka adalah momen yang setara dalam waktu.
Eero
Pernyataan sedangkan Time.at(1318996912) # => 2011-10-19 00:01:52 -0400menampilkan nilai kembali dalam zona waktu LOKAL tampaknya tidak akurat ... Tolong verifikasi? Saya percaya pernyataan Anda hanya akan benar jika Anda menggunakanTime.zone.at(1318996912)
BigRon
Ya, itu tampaknya akurat. Mesin lokal saya disetel ke EST dan waktu ditampilkan dalam EST.
WattsInABox
Bisakah Anda memberikan contoh di mana ini bukan kasus @BigRon? Zona waktu apa, versi ruby, dll. Yang tidak berlaku seperti ini?
WattsInABox
10

Satu perintah untuk mengubah waktu tanggal ke format Unix dan kemudian ke string

    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

akhirnya strftime digunakan untuk memformat tanggal

Contoh:

    irb(main):034:0> DateTime.strptime("1410321600",'%s').strftime("%d %m %y")
    "10 09 14"
Tejasvi Manmatha
sumber
Satu hal yang perlu diperhatikan adalah bahwa format zaman tidak memiliki zona waktu sehingga chaining utc sebelum chaining to_i tidak diperlukan di Time.now.utc.to_i.
Trevor McCasland
1

Ini memberi tahu Anda tanggal jumlah detik di masa mendatang sejak Anda menjalankan kode.

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

menempatkan (waktu)

sesuai dengan waktu saat ini saya menjawab pertanyaan yang dicetaknya 047-05-14 05:16:16 +0000(1 miliar detik di masa depan)

atau jika Anda ingin menghitung miliar detik dari waktu tertentu, itu dalam format Time.mktime(year, month,date,hours,minutes)

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

menempatkan ("Saya akan berusia 1 Miliar detik pada:" + waktu)

sbutterworth
sumber
0

Jika Anda ingin hanya Tanggal, Anda dapat melakukan Date.strptime(invoice.date.to_s, '%s')di mana invoice.datedatang dalam bentuk Fixnumdan kemudian dikonversi ke String.

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