问题
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:
Add a custom method to the
Integer
class. You can create a new file calledpretty_duration.rb
in theinitializers
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
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