当我在 sum_time 方法中使用 guard case 时,一个测试用例失败了,有什么建议吗?

When I used guard case in sum_time method one test case failed, any suggestions?

编写一个方法,对多个 24 小时时间值 (h:m:s) 求和。使用正则表达式验证时间。

输入输出格式: 例如:("11:23:07","22:53:45","0:23:23","23:45:56") -> "2 day & 10:26:11" ("11:23:07") -> "11:23:07"

我的代码因一个测试用例而失败,即 “24:01:10” “10:30:50”

解决方案:

require 'time'
# Time_Sum
class Time
  KEY = /^(0\d|1\d|2[0-3]|\d):[0-5]\d?:[0-5]\d?$/
  def to_seconds(timestamp)
    timestamp.hour * 3600 + timestamp.min * 60 + timestamp.sec
  end

  def validate?(time_string)
    time_string.match(KEY)
  end

  def sum_time(*time)
    total_seconds = 0
    time.each do |time_item|
      timestamp = Time.parse(time_item) if validate?(time_item)
      total_seconds += to_seconds(timestamp) if validate?(time_item)
      'Invalid 24-hour time value'
    end
    display_results(total_seconds)
  end

  def display_results(total_seconds)
    sum_time_string = ''
    days = (total_seconds / (24 * 3600)).to_i
    sum_time_string = "#{days} day & " if days > 0
    sum_time_string + Time.at(total_seconds).utc.strftime('%H:%M:%S')
  end
end
reader = Time.new
if ARGV.empty?
  puts 'Please provide an input'
else
  p reader.sum_time(*ARGV)
end

预期输出: "Invalid 24-hour time value" 实际输出: “10:30:50”

还有人可以建议更好的正则表达式密钥吗?

首先,我建议不要污染核心 Ruby class Time。所需的方法可以包含在自定义 class 中,或者可能定义在 main 级别(正如我在下面所做的那样)。

我也不会使用 classes TimeDateTime。处理字符串更容易。首先构建一个正则表达式,用于确定每个时间字符串是否有效。

VALID_TIME = /
             \A        # match beginning of string
             (?:       # begin non-capture of group
               0?\d    # optionally match a zero followed by a digit
               |       # or
               1\d     # match 1 followed by a digit
               |       # or
               2[0-3]  # match 2 followed by a digit 0, 1, 2 or 3
             )         # end non-capture group
             (?:       # begin a non-capture group
               :       # match a colon
               [0-5]   # match a digit 0, 1, 2, 3, 4 or 5
               \d      # match a digit
             )         # end non-capture group
             {2}       # execute the previous non-capture group twice
             /x        # free-spacing regex-definition mode

以free-spacing 模式编写正则表达式的优点是使它们成为self-documenting。这个正则表达式按照惯例写成:

/\A(?:0?\d|1\d|2[0-3])(?::[0-5]\d){2}/

我们的主要方法可能是 total_times,向其传递一个或多个表示 twenty-four 小时的字符串。

def total_times(*time_strings)
  seconds    = tot_seconds(*time_strings)
  days, hrs  = seconds.divmod(24*3600)
  hrs,  mins = hrs.divmod(3600)
  mins, secs = mins.divmod(60)
  [days, hrs, mins, secs]
end

这首先调用一个方法 tot_seconds,具有相同的参数。该方法 returns 自午夜以来经过的每次秒数的总和。获得总秒数后,重复使用 Integer#divmod 计算等效的天数、小时数、分钟数和秒数。

def tot_seconds(*time_strings)
  time_strings.sum { |time_str| time_str_to_seconds(time_str) }
end

此方法将每个时间字符串传递给方法 time_str_to_seconds,其中 returns 该时间字符串自午夜以来的秒数。

def time_str_to_seconds(time_str)
  raise ArgumentError, "'#{time_str}' is an invalid 24-hour time value" unless
    time_str.match?(VALID_TIME)
  [3600, 60, 1].zip(time_str.split(':')).
                reduce(0) { |tot,(n,s)| tot + n*s.to_i }
end

此方法首先检查时间字符串的有效性。如果发现它无效,则会引发异常。这似乎是进行有效性检查的最佳位置。如果发现字符串有效,则将其拆分为表示小时、分钟和秒的字符串,然后将其转换为整数并组合以获得秒数。

假设:

time_str = "9:35:08"

那么time_str_to_seconds中的计算如下:

time_str.match?(VALID_TIME)
  #=> true
a = time_str.split(':')
  #=> ["9", "35", "08"] 
b = [3600, 60, 1].zip(a)
  #=> [[3600, "9"], [60, "35"], [1, "08"]]
b.reduce(0) { |tot,(n,s)| tot + n*s.to_i }
  #=> 3600*9 + 60*35 + 1*8 => 34508

参见 String#match? and Array#zip

现在让我们用一个例子来试试这个。

arr = total_times "2:33:41", "23:46:08"
  #=> [1, 2, 19, 49]

我们可能会使用这些结果如下:

puts "%d day & %d:%2d:%2d" % arr
"1 day & 2:19:49"

在某些情况下,最好写成:

days, hrs, mins, secs = total_times "2:33:41", "23:46:08"

我们可以很容易地确认这些结果。

tot_secs =  2*3600 + 33*60 + 41 +
           23*3600 + 46*60 +  8
  #=> 94789 
days, hrs  = tot_secs.divmod(24*3600)
  #=> [1, 8389] 
hrs, mins  = hrs.divmod(3600)
  #=> [2, 1189] 
mins, secs = mins.divmod(60)
  #=> [19, 49] 
[days, hrs, mins, secs] 
  #=> [1, 2, 19, 49] 

另外两个例子:

total_times "11:23:07", "22:53:45", "0:23:23", "23:45:56"
  #=> [2, 10, 26, 11]
total_times "24:01:10", "10:30:50"
  #=> ArgumentError ('24:01:10' is an invalid 24-hour time value)

请注意,不一定要使用正则表达式来确定时间字符串的有效性。可以这样写:

time_str = "9:35:08"
hr, min, sec = time_str.split(':').map(&:to_i)
  #=> [9, 35, 8] 
(0..23).cover?(hr) && (0..59).cover?(min) && (0..59).cover?(sec)
  #=> true