Convert duration to hours:minutes:seconds (or similar) in Rails 3 or Ruby

China☆狼群 提交于 2020-07-04 05:29:09

问题


I have a feeling there is a simple/built-in way to do this but I can't find it.

I have a duration (in seconds) in an integer and I want to display it in a friendly format.

e.g. 3600 would be displayed as "01:00:00" or "1 hour" or something.

I can do it with time_ago_in_words(Time.zone.now+3600) but that feels like a bit of a hack, there is no reason to add/subtract from the current time just to format this value. Is there a duration_in_words() or something?

Thanks


回答1:


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

distance_of_time_in_words(3600)
 => "about 1 hour"



回答2:


Summing up:

assuming that total_seconds = 3600

Option 1:

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

Option 2:

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

Option 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"

use Option1 if you want words, Option2 if you want H:M:S format, Option3 if you want H:M:S format and there can be more than 24 hours




回答3:


Ruby's string % operator is too unappreciated and oft forgotten.

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

Given t is a duration in seconds, this emits a zero-padded colon-separated string including days. Example:

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

Lovely.




回答4:


I guess you could do also something like:

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

To format it as you wish.

BTW, originally I thought of using Time.at() but seems that EPOCH time on my Ubuntu is Thu Jan 01 01:00:00 +0100 1970 and not 00:00:00 hours as I expected, and therefore if I do:

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

Gives me 1 hour more than wanted.




回答5:


I use this to show time durations in my Rails Project:

  1. Add a custom method to the Integer class. You can create a new file called pretty_duration.rb in the initializers folder:

    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. Call seconds.pretty_duration anywhere in your project:

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

This answer builds up on Lev Lukomsky's Code




回答6:


This one uses the obscure divmod method to divide and modulo at the same time, so it handles Float seconds properly:

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



回答7:


Be careful with the duration longer than one day.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")



回答8:


Using Time.utc.strftime works only for values when total number of hours is less then 24:

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

For greater values it returns incorrect results:

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

I suggest using the simplest method I found for this problem:

  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

You can always adjust returned value to your needs.




回答9:


An answer inspired from Lev Lukomsky's one taking advantage of ActiveSupport::Duration, and handling milliseconds (useful to benchmark code)

# 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"

Of course you can extend this easily with days, months, years, etc using related ActiveSupport methods and repeating the same structure.

Keep in mind that for too long durations, this may be inaccurate since the duration of 1 month is not fixed in number of days, and I'm not sure how AS:Duration deals with that.




回答10:


Just to throw in my 2 cents:

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

Built off of Xiao Bin's answer.




回答11:


Shout out to @joshuapinter who gave the best answer (in the form of a comment).

Use the drop-in replacement dotiw gem to gain more control over the accuracy of the output to suit different needs:

https://github.com/radar/distance_of_time_in_words

Sample view code:

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

Resulting in something like this:

Logoff after: 1 day, 13 hours, and 20 minutes



回答12:


ActiveSupport::Duration.build + inspect gives you valid results

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"



回答13:


    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"


来源:https://stackoverflow.com/questions/4175733/convert-duration-to-hoursminutesseconds-or-similar-in-rails-3-or-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!