Ubah durasi menjadi jam: menit: detik (atau serupa) di Rails 3 atau Ruby

115

Saya merasa ada cara sederhana / bawaan untuk melakukan ini tetapi saya tidak dapat menemukannya.

Saya memiliki durasi (dalam detik) dalam bilangan bulat dan saya ingin menampilkannya dalam format yang bersahabat.

misalnya 3600 akan ditampilkan sebagai "01:00:00" atau "1 jam" atau semacamnya.

Saya dapat melakukannya dengan time_ago_in_words(Time.zone.now+3600)tetapi itu terasa seperti sedikit retakan, tidak ada alasan untuk menambah / mengurangi dari waktu saat ini hanya untuk memformat nilai ini. Apakah ada duration_in_words()atau sesuatu?

Terima kasih

cydonia
sumber

Jawaban:

83

Lihat: http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html

distance_of_time_in_words(3600)
 => "about 1 hour"
allan
sumber
1
terima kasih saya melihat itu tetapi pikir saya harus menyediakan dua kali .. apa inti!
cydonia
1
ya, contoh-contoh itu konyol
allan
1
distance_of_time_in_words (from_time, to_time, ...)
boulder_ruby
1
Selain itu, jika Anda ingin ini menjadi sangat spesifik dan tidak "membulatkan" durasinya, periksa permata Radar: github.com/radar/distance_of_time_in_words . Drop-in pengganti distance_of_time_in_wordsdan Anda bisa mendapatkan angka yang dibulatkan dengan mengoper vague: truesebagai opsi.
Joshua Pinter
194

Menyimpulkan:

berasumsi bahwa total_seconds = 3600

Pilihan 1:

distance_of_time_in_words(total_seconds) #=> "about 1 hour"

Pilihan 2:

Time.at(total_seconds).utc.strftime("%H:%M:%S") #=> "01:00:00"

Opsi 3:

seconds = total_seconds % 60
minutes = (total_seconds / 60) % 60
hours = total_seconds / (60 * 60)

format("%02d:%02d:%02d", hours, minutes, seconds) #=> "01:00:00"

gunakan Option1 jika anda menginginkan kata-kata, Option2 jika anda ingin format H: M: S, Option3 jika anda ingin format H: M: S dan bisa lebih dari 24 jam

Lev Lukomsky
sumber
1
Metode distance_of_time_in_words adalah ActionView Helper, sehingga perlu dipanggil dari View (bukan controller). api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar
13
Perhatikan bahwa opsi strftime akan meluap pada 24 jam. Jika durasi Anda 25 jam, itu akan ditampilkan 01:00:00.
Gabe Martin-Dempesy
2
ini tidak berfungsi untuk waktu negatif, misalnya -1800 mengembalikan -1j 30m bukannya -30m
Arnold Roa
45

%Operator tali Ruby terlalu tidak dihargai dan sering dilupakan.

"%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]

Mengingat t adalah durasi dalam detik, ini memancarkan string yang dipisahkan oleh titik dua dengan bantalan nol termasuk hari. Contoh:

t = 123456
"%02d:%02d:%02d:%02d" % [t/86400, t/3600%24, t/60%60, t%60]
=> "01:10:17:36"

Menyenangkan.

IAmNaN
sumber
24
Itu definisi aneh dari "cantik".
Marc Bollinger
10
The %mengatakan, "Ambillah array parameter dari kanan saya dan memasukkan mereka ke dalam format string di sebelah kiri saya." Dalam hal ini, mirip dengan printfdi C \ C ++ hanya lebih ringkas dan dapat digunakan dalam sebuah tugas. Ya, saya menyebutnya indah. Dan kuat. Ada contoh yang akan mendemonstrasikan kefasihan ini dengan lebih baik, tetapi mereka tidak akan menjawab pertanyaannya. Snark Anda tidak dihargai, btw.
IAmNaN
26

Saya rasa Anda juga bisa melakukan sesuatu seperti:

(Time.mktime(0)+3600).strftime("%H:%M:%S")

Untuk memformatnya sesuai keinginan.

BTW, awalnya saya berpikir untuk menggunakan Time.at () tetapi tampaknya waktu EPOCH di Ubuntu saya adalah Kam 01 Jan 01:00:00 +0100 1970 dan bukan jam 00:00:00 seperti yang saya harapkan, dan oleh karena itu jika saya melakukannya:

Time.at(3600).strftime("%H:%M:%S")

Memberi saya 1 jam lebih dari yang diinginkan.

Cristobal Viedma
sumber
ahh, mungkin ada yang bisa dilakukan karena waktu musim panas?
Cristobal Viedma
7
Satu-satunya masalah adalah jika Anda berurusan dengan waktu lebih dari 24 jam. Dalam hal ini, jam akan berputar kembali ke angka yang rendah.
metavida
Untuk waktu yang lebih dari 24 jam,% j akan memberi Anda jumlah hari (selama kurang dari 365)
BananaNeil
18

Saya menggunakan ini untuk menunjukkan durasi waktu di Proyek Rails saya:

  1. Tambahkan metode khusus ke Integerkelas. Anda dapat membuat file baru bernama pretty_duration.rbdalam initializersfolder:

    class Integer
        def pretty_duration
            parse_string = 
                if self < 3600
                    '%M:%S'
                else
                    '%H:%M:%S'
                end
    
            Time.at(self).utc.strftime(parse_string)
        end
    end
  2. Hubungi seconds.pretty_durationdi mana saja dalam proyek Anda:

    275.pretty_duration     # => "04:35"
    9823.pretty_duration    # => "02:43:43"

Jawaban ini dibangun di atas Kode Lev Lukomsky

Sheharyar
sumber
Bekerja dengan baik. Terima kasih untuk ini.
Donn Felker
13

Yang ini menggunakan divmodmetode tidak jelas untuk membagi dan modulo pada saat yang sama, sehingga menangani Floatdetik dengan benar:

def duration(seconds)
  minutes, seconds = seconds.divmod(60)
  hours, minutes = minutes.divmod(60)
  days, hours = hours.divmod(24)

  "#{days.to_s.rjust(3)}d #{hours.to_s.rjust(2)}h #{minutes.to_s.rjust(2)}m #{seconds}s"
end
Brent Royal-Gordon
sumber
6

Menggunakan Time.utc.strftimehanya berfungsi untuk nilai-nilai ketika jumlah total jam kurang dari 24 :

2.2.2 :004 > Time.at(60 * 60).utc.strftime('%H h %M m')
=> "01 h 00 m"

Untuk nilai yang lebih besar, hasilnya salah:

2.2.2 :006 > Time.at(60 * 60 * 24).utc.strftime('%H h %M m')
 => "00 h 00 m"

Saya sarankan menggunakan metode paling sederhana yang saya temukan untuk masalah ini:

  def formatted_duration total_seconds
    hours = total_seconds / (60 * 60)
    minutes = (total_seconds / 60) % 60
    seconds = total_seconds % 60
    "#{ hours } h #{ minutes } m #{ seconds } s"
  end

Anda selalu dapat menyesuaikan nilai yang dikembalikan dengan kebutuhan Anda.

Igor Springer
sumber
5

Hati-hati dengan durasinya lebih dari satu hari.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
Xiao Bin
sumber
Berikan contoh input / output yang menyoroti bagaimana ini menyelesaikan kasus sudut
Cyril Duchon-Doris
4

Jawaban yang terinspirasi dari Lev Lukomsky yang memanfaatkan ActiveSupport :: Durasi, dan menangani milidetik (berguna untuk kode benchmark)

# duration in ms modulus number of ms in one second
milliseconds = duration.in_milliseconds % 1.second.in_milliseconds

# duration in seconds modulus number of seconds in one minute
seconds = (duration / 1.second) % (1.minute / 1.second)

# duration in minutes modulus number of minutes in one hour
minutes = (duration / 1.minute) % (1.hour / 1.minute)

# duration in hours modulus number of hours in one day
hours = (duration / 1.hour) % (1.day / 1.hour)

format("%02d:%02d:%02d:%03d", hours, minutes, seconds, milliseconds) #=> "12:05:00:001"

Tentu saja Anda dapat memperpanjang ini dengan mudah dengan hari, bulan, tahun, dll menggunakan metode ActiveSupport terkait dan mengulangi struktur yang sama.

Perlu diingat bahwa untuk durasi yang terlalu lama, ini mungkin tidak akurat karena durasi 1 bulan tidak tetap dalam jumlah hari, dan saya tidak yakin bagaimana AS: Durasi menangani hal itu.

Cyril Duchon-Doris
sumber
2

Hanya untuk memasukkan 2 sen saya:

Time.at(i).utc.strftime((i < 3600) ? '%-M minutes and %-S seconds' : '%-H hours, %-M minutes, and %-S seconds')

Dibangun dari jawaban Xiao Bin.

Daniel
sumber
2

Teriak ke @joshuapinter yang memberikan jawaban terbaik (berupa komentar ).

Gunakan pengganti drop-in dotiw permata untuk mendapatkan kontrol lebih besar atas keakuratan output agar sesuai dengan kebutuhan yang berbeda:

https://github.com/radar/distance_of_time_in_words

Contoh kode tampilan:

%label
  Logoff after:
  - expire_in = distance_of_time_in_words(Time.now, Time.now + user.custom_timeout.minutes, :only => [:minutes, :hours, :days])
  = expire_in

Menghasilkan sesuatu seperti ini:

Logoff after: 1 day, 13 hours, and 20 minutes
Jon Kern
sumber
2

ActiveSupport::Duration.build+ inspectmemberi Anda hasil yang valid

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"
Sathish
sumber
1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"
Luciano Ribas
sumber
5
Harap tambahkan beberapa konteks pada jawabannya.
Paul Dawson