Ruby добавляет/вычитает только секунды, минуты, часы

Как я могу начать со строки HH:MM:SS Ruby, например «00:00:08», и объединить ее с «-00:00:06», чтобы получить разницу, которая в этом случае будет «00:00:02» случай?

Все, что я пытаюсь найти, включает преобразование в DateTime, но действительно ли мне нужно потратить дни или годы, чтобы выяснить относительную разницу?

Вам это не нужно, но использование DateTime кажется самым простым способом.

Fravadona 12.06.2024 07:44

Наверное, я не понимаю, как это моделировать с помощью DateTime.

kraftydevil 12.06.2024 07:49

@kraftydevil: Time.strptime('00:00:08', '%H:%M:%s')-Time.strptime('00:00:06', '%H:%M:%s')? Возвращает 2.0, которое вы можете преобразовать обратно во время или просто выполнить простые математические вычисления, чтобы самостоятельно отформатировать его как строку.

ShadowRanger 13.06.2024 04:11

@ShadowRanger %s — количество секунд с начала эпохи, оно переопределяет все остальное. используйте %S.

Alex 13.06.2024 08:02

@Алекс: Ах, упс. Вот что я получаю за то, что не перепроверил документы. Спасибо за исправление.

ShadowRanger 13.06.2024 23:06
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
2
5
148
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Если вы хотите избежать DateTime, вы можете вручную проанализировать время и преобразовать его в секунды.

def to_seconds(str)
  neg = str =~ /^\-/

  str = str.sub(/^\-/, '')
    
  (neg ? -1 : 1) * str
    .split(':')
    .map(&:to_i)
    .reverse
    .each_with_index
    .sum  { |x, i| x * 60 ** i }
end

Складывать или вычитать секунды легко. На этом этапе вам просто нужно отменить процесс, чтобы вернуться к отформатированной метке времени в часах/минутах/секундах.

Создайте класс для анализа вашей строки в секундах, определите любые необходимые операции, такие как + и -, и отформатируйте обратно в строковое представление из секунд. Наличие отрицательного знака в строке требует немного дополнительной руки:

class Tyme
  def initialize str
    @negative = str.start_with?("-")
    str = str.sub(/-/, "")
    @h, @m, @s = str.split(":").map(&:to_i)
    @total = @h * 3600 + @m * 60 + @s
  end

  def total
    @negative ? -@total : @total
  end

  def +(other)
    format total + other.total
  end

  def -(other)
    format total - other.total
  end

  private

  def format total_seconds
    minutes, seconds = total_seconds.abs.divmod(60)
    hours, minutes = minutes.divmod(60)
    fmt = "%02d:%02d:%02d" % [hours, minutes, seconds]
    total_seconds.negative? ? "-#{fmt}" : fmt
  end
end
>> Tyme.new("00:00:08") + Tyme.new("-00:00:02")
=> "00:00:06"
>> Tyme.new("00:00:08") - Tyme.new("00:00:02")
=> "00:00:06"
>> Tyme.new("00:00:08") - Tyme.new("-00:00:02")
=> "00:00:10"

@Стефан рефакторинг

class Duration
  def self.parse str
    negative = str.start_with?("-") ? -1 : 1
    str = str.sub(/-/, "")
    h, m, s = str.split(":").map(&:to_i)
    total = negative * (h * 3600 + m * 60 + s)
    new total
  end

  def initialize seconds
    @seconds = seconds
  end

  attr_reader :seconds

  def +(other)
    Duration.new seconds + other.seconds
  end

  def -(other)
    Duration.new seconds - other.seconds
  end

  def to_s
    minutes, seconds = @seconds.abs.divmod(60)
    hours, minutes = minutes.divmod(60)
    fmt = "%02d:%02d:%02d" % [hours, minutes, seconds]
    @seconds.negative? ? "-#{fmt}" : fmt
  end

  def inspect
    "#<Duration #{to_s}>"
  end
end
>> Duration.parse("00:00:08") + Duration.parse("-00:00:02")
=> #<Duration 00:00:06>
>> Duration.parse("00:00:08") - Duration.parse("00:00:02")
=> #<Duration 00:00:06>
>> Duration.parse("00:00:08") - Duration.parse("-00:00:02")
=> #<Duration 00:00:10>

Несколько предложений: сделайте так, чтобы initialize взял количество секунд и определил отдельный self.parse для анализа строк. Измените + и -, чтобы возвращать новые экземпляры Tyme вместо строк. Переименуйте format в to_s/inspect. (возможно, переименуйте Tyme в Duration)

Stefan 12.06.2024 11:48

Вот еще один вариант:

def time_to_seconds(time)
  ord,hours,min,seconds = time.scan(/(-|\+)?(\d+):(\d{1,2}):(\d{1,2})/).flatten
  result = [3600,60,1].zip([hours,min,seconds].map(&:to_i)).sum{|a| a.reduce(:*)}
  result.send("#{ord || '+'}@")
end 

def diff_times(t1,t2)
  diff_seconds = time_to_seconds(t1) + time_to_seconds(t2)
  ord = '-' if diff_seconds < 0
  result = [3600,60,1].map {|e| r,diff_seconds = diff_seconds.abs.divmod(e); r }
  sprintf "#{ord}%02d:%02d:%02d", *result
end 

t1 = "00:00:08"
t2 = "-00:00:06"

diff_times(t1,t2)
#=> "00:00:02"

Сначала мы преобразуем каждое время в секунды, используя регулярное выражение для захвата каждой группы часов, минут и секунд, учитывая + или -.

Затем мы складываем их вместе, чтобы получить продолжительность в секундах.

Наконец, мы конвертируем их обратно в массив [hours, minutes,seconds] и используем Kernel#sprintf для форматирования вывода String

Ответ принят как подходящий

Чтобы сбалансировать читабельность, тестируемость и компактность, я предлагаю следующие три метода. Я не думаю, что сложность вычислений оправдывает создание класса.

Преобразование строк в секунды

def str_to_seconds(str)
  tot = str.match(/(\d{2,}):(\d{2}):(\d{2})(?!\d)/)
           .captures
           .zip([3600, 60, 1])
           .sum { |s,d| s.to_i * d }
  str[0] == '-' ? -tot : tot
end

Некоторые примеры:

puts "      str        str_to_seconds(str)"
puts "------------------------------------"
["00:00:07", "00:00:37", "00:02:07", "00:58:02", "02:00:07",
 "06:01:02", "50:00:07", "130:00:07", "-130:00:07", "00:00:011"].each do |str|
  print "%11s" % str
  puts "   %15d" % str_to_seconds(str)
end
      str        str_to_seconds(str)
------------------------------------
   00:00:07                 7
   00:00:37                37
   00:02:07               127
   00:58:02              3482
   02:00:07              7207
   06:01:02             21662
   50:00:07            180007
  130:00:07            468007
 -130:00:07           -468007
   00:00:011
  NoMethodError: undefined method `captures' for nil:NilClass

Преобразование секунд в строки

def seconds_to_str(secs)
  abs_secs = secs.abs
  hrs, abs_secs = abs_secs.divmod(3600)
  mins, abs_secs = abs_secs.divmod(60)
  str = "%02d:%02d:%02d" % [hrs, mins, abs_secs]
  secs < 0 ? "-#{str}" : str
end

Некоторые примеры:

puts "      secs     seconds_to_str(secs)"
puts "-----------------------------------"
[7, 37, 127, 3482, -7207, 21662, 180007, 468007, -468007].each do |secs|
  puts "%11d   %14s" % [secs, seconds_to_str(secs)]
end
      secs     seconds_to_str(secs)
-----------------------------------
          7         00:00:07
         37         00:00:37
        127         00:02:07
       3482         00:58:02
      -7207        -02:00:07
      21662         06:01:02
     180007         50:00:07
     468007        130:00:07
    -468007       -130:00:07

Вычислите желаемый результат

def compute(str1, str2)
  seconds_to_str(str_to_seconds(str1) + str_to_seconds(str2))
end

Некоторые примеры:

puts "    str1        str2     compute(str1, sz)"
puts "------------------------------------------"
[[ "00:00:45", "00:00:17"], [" 00:00:45", "-00:00:17"],
 ["-00:00:45", "00:00:17"], ["-00:00:45", "-00:00:17"],
 [" 16:12:05", "13:13:06"], [ "16:12:05", "-13:13:06"],
 ["-16:12:05", "13:13:06"], ["-16:12:05", "-13:13:06"],].each do |str1, str2|
  puts "%10s  %10s      %10s" % [str1, str2, compute(str1, str2)]
end
    str1        str2     compute(str1, sz)
------------------------------------------
  00:00:45    00:00:17        00:01:02
  00:00:45   -00:00:17        00:00:28
 -00:00:45    00:00:17       -00:00:28
 -00:00:45   -00:00:17       -00:01:02
  16:12:05    13:13:06        29:25:11
  16:12:05   -13:13:06        02:58:59
 -16:12:05    13:13:06       -02:58:59
 -16:12:05   -13:13:06       -29:25:11

@engineersmnky, спасибо за то, что твой орлиный взгляд снова обратил внимание на мой ответ. Я сделал редактирование.

Cary Swoveland 14.06.2024 02:13

Другие вопросы по теме