Преобразование продолжительности в часы: минуты: секунды (или аналогичные) в Rails 3 или Ruby

115

У меня есть ощущение, что есть простой / встроенный способ сделать это, но я не могу его найти.

У меня есть целое число (в секундах), и я хочу отобразить его в удобном формате.

например, 3600 будет отображаться как «01:00:00» или «1 час» или что-то в этом роде.

Я могу это сделать, time_ago_in_words(Time.zone.now+3600)но это похоже на взлом, нет причин добавлять / вычитать из текущего времени только для форматирования этого значения. Есть что- duration_in_words()нибудь?

Спасибо

Сидонии
источник

Ответы:

83

См. Http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html

distance_of_time_in_words(3600)
 => "about 1 hour"
Аллан
источник
1
спасибо, я посмотрел на это, но подумал, что мне нужно предоставить два раза ... что за кусок!
cydonia
1
да, это глупые примеры
allan
1
distance_of_time_in_words (from_time, to_time, ...)
boulder_ruby
1
Кроме того, если вы хотите, чтобы это было очень конкретным, а не «округляло» продолжительность, посмотрите жемчужину Radar: github.com/radar/distance_of_time_in_words . Прямая замена для, distance_of_time_in_wordsи вы можете получить округленное число, передав vague: trueкак опцию.
Джошуа Пинтер
194

Подводя итоги:

при условии, что total_seconds = 3600

Опция 1:

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

Вариант 2:

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

Вариант 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"

используйте Option1, если вам нужны слова, Option2, если вы хотите формат H: M: S, Option3, если вы хотите формат H: M: S, и может быть более 24 часов

Лев Лукомский
источник
1
Метод distance_of_time_in_words является помощником ActionView, поэтому его нужно вызывать из View (а не из контроллера). api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html
msdundar
13
Обратите внимание, что параметр strftime переполнится через 24 часа. Если ваша продолжительность составляет 25 часов, она будет отображаться 01:00:00.
Гейб Мартин-Демпеси,
2
это не работает для отрицательных значений времени, например -1800 возвращает -1 час 30 минут вместо -30 минут
Арнольд Роа
45

Строковый %оператор Ruby слишком недооценен и часто забывается.

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

Учитывая, что t - это продолжительность в секундах, это выдает строку с разделителями-двоеточиями, дополненную нулями, включая дни. Пример:

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

Прекрасный.

IAmNaN
источник
24
Странное определение слова «прекрасный».
Марк Боллинджер,
10
Он %говорит: «Возьмите массив параметров справа и вставьте их в строку формата слева от меня». В этом отношении он похож на printfC \ C ++, только более краток и может использоваться в назначении. Да, я называю это прекрасным. И мощный. Есть примеры, которые лучше демонстрируют это красноречие, но они не дают ответа на вопрос. Между прочим, твои язвы не оценены.
IAmNaN
26

Я думаю, вы могли бы сделать что-то вроде:

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

Отформатировать как хотите.

Кстати, изначально я думал об использовании Time.at (), но кажется, что время EPOCH на моем Ubuntu - четверг 01 января 01:00:00 +0100 1970, а не 00:00:00 часов, как я ожидал, и поэтому, если я это сделаю:

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

Дает мне на 1 час больше, чем хотелось.

Кристобаль Вьедма
источник
ааа, может чем заняться из-за летнего времени?
Cristobal Viedma
7
Единственная загвоздка в том, что вы имеете дело со временем, превышающим 24 часа. В этом случае часы вернутся к меньшему числу.
metavida
Для времени, превышающего 24 часа,% j предоставит вам количество дней (если оно меньше 365)
BananaNeil
18

Я использую это, чтобы показать продолжительность времени в моем проекте Rails:

  1. Добавьте в Integerкласс собственный метод . Вы можете создать новый файл с именем pretty_duration.rbв initializersпапке:

    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. Звоните в seconds.pretty_durationлюбую точку вашего проекта:

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

Этот ответ основан на Кодексе Льва Лукомского.

Sheharyar
источник
Сработало отлично. Спасибо тебе за это.
Донн Фелкер
13

Этот использует неясный divmodметод для одновременного деления и по модулю, поэтому он Floatправильно обрабатывает секунды:

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
Брент Роял-Гордон
источник
6

Использование Time.utc.strftimeработает только для значений, когда общее количество часов меньше 24 :

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

Для больших значений возвращает неверные результаты:

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

Я предлагаю использовать самый простой способ, который я нашел для этой проблемы:

  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

Вы всегда можете настроить возвращаемое значение в соответствии с вашими потребностями.

Игорь Спрингер
источник
5

Будьте осторожны с продолжительностью более одного дня.

(timing/3600).to_i.to_s.rjust(2,'0') + ":"+Time.at(timing).utc.strftime("%M:%S")
Сяо Бин
источник
Приведите пример ввода / вывода, подчеркнув, как это решает угловой случай
Сирил Дюшон-Дорис
4

Ответ, вдохновленный тем, что Лев Лукомский использует ActiveSupport :: Duration и обрабатывает миллисекунды (полезно для тестирования кода)

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

Конечно, вы можете легко увеличить это количество дней, месяцев, лет и т. Д., Используя соответствующие методы ActiveSupport и повторяя ту же структуру.

Имейте в виду, что для слишком большой продолжительности это может быть неточно, поскольку продолжительность 1 месяца не фиксируется в количестве дней, и я не уверен, как с этим справляется AS: Duration.

Сирил Дюшон-Дорис
источник
2

Просто чтобы добавить свои 2 цента:

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

Построен на основе ответа Сяо Биня.

Даниэль
источник
2

Выкрикните к @joshuapinter который дал лучший ответ (в виде комментария ).

Используйте заменяющий dotiwдрагоценный камень, чтобы получить больший контроль над точностью вывода в соответствии с различными потребностями:

https://github.com/radar/distance_of_time_in_words

Пример кода просмотра:

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

В результате получается что-то вроде этого:

Logoff after: 1 day, 13 hours, and 20 minutes
Джон Керн
источник
2

ActiveSupport::Duration.build+ inspectдает вам достоверные результаты

 >> ActiveSupport::Duration.build(125557).inspect
 => "1 day, 10 hours, 52 minutes, and 37 seconds"
Sathish
источник
1
    hours = 3.5456
    value = (hours*60).divmod(60).map{ |a| "%02d"%[a.floor] }.join(":")
    => "03:32"
Лучано Рибас
источник
5
Пожалуйста, добавьте контекст к ответу.
Пол Доусон