Adding minutes to a time value in Ruby I recently submitted a code-challenge for an interview according to the following guidelines:

About

Write a function or method that accepts two mandatory arguments and returns a result.

Requirements:


Use Ruby or another language of your choosing, and write production quality code.
Your function or method should not use any date/time library.
The first argument is a time value in the form of a string with the following format: [H]H:MM {AM|PM}
The second argument is the number of minutes, as an integer value
The return value of the function should be a string of the same format as the first
argument, which is a result of adding the minutes to the time.


For example, add_minutes('9:13 AM', 10) would return 9:23 AM



Additional notes:


We just want to see how you code, so this exercise is not meant to be too hard or take too long.
If you spend an hour on this, stop coding and finalize by adding some notes about what you would do if you had more time.


Although my Ruby proficiency has somewhat atrophied due to primarily working with php for the last 8 months, I was able to produce the following well-within one-hour of beginning the challenge:

# FIRST ATTEMPT

# require 'time'

# def flux_capacitor(time,mins)
#   the_time = Time.parse(time.to_s)
#   mins = the_time + 10*60
# end


# FINAL REFACTOR

def flux_capacitor(time,mins)
  the_time = time.scan(/\d/).join('').to_i
  meridian = time.scan(/(A|M|P)/).join('')
  new_time = the_time + mins 
  back_in_time = new_time.to_s.insert(-3,':') + " #{meridian}"
end 

puts flux_capacitor("9:13 AM",10)
puts flux_capacitor("9:13 PM",10)
p flux_capacitor("10:13 PM",10).class

# DOX

# http://ruby-doc.org/core-2.2.3/Time.html
# https://www.ruby-forum.com/topic/125709
# https://www.ruby-forum.com/topic/104359
# http://apidock.com/ruby/String/insert
# http://rubular.com/


Simply-stated, is there a better approach?  I've asked others and they think my solution is fairly clean.  Obviously, I know my solution could benefit with a conditional statement for edge cases (e.g. flux_capacitor("9:50 AM", 15)).
More testing is required:
flux_capacitor(&quot;11:59 AM&quot;, 62)
 =&gt; &quot;12:21 AM&quot; 

Definitely test some corner cases.

Updated Thoughts
I think you should completely rethink the way you are handling time.
Use a regex to validate that time is a valid string, then simply use string.split to get each portion of the time: hours, minutes, and AM/PM.
Next you should convert those into a quantity of minutes:
hours += 12 if meridian == 'PM'
total_minutes = hours*60+minutes+mins

Next I would recalculate the time as a 24-hour time (which goes up to 23 hours, 59 minutes):
# this is integer math so the decimal portion will be discarded
# adjusted_minutes may be greater than 23, but adjusted_minutes should always be less than 60
adjusted_hours = (total_minutes / 60)
adjusted_minutes = total_minutes - (60*adjusted_hours)

# modulus 24 converts adjusted_hours into 24-hour time
adjusted_hours = adjusted_hours % 24

Now convert to 12 hour time if necessary and determine if this is AM or PM
# the added mins may have been several days worth, so adjusted_hours might be very large.

if adjusted_hours &gt; 12
    meridian = &quot;PM&quot;
    adjusted_hours -= 12
else
    meridian = &quot;AM&quot;
end

Finally return a string of the new time:
# zero pad the minutes, as this is expected for a time
return &quot;%d:%02d %s&quot; % [adjusted_hours, adjusted_minutes, meridian]


Final Solution
Since a solution that reliably passes all test cases has yet to be put forth...
def flux_capacitor(time, mins)
    #check if time in valid format
    time_match = time.strip.match /^(12|11|10|0?\d):([012345]\d)\s+(AM|PM)/
    
    #throw error on invalid time
    raise(ArgumentError, &quot;Invalid time: #{time.strip}&quot;) if not time_match
    
    #calculate new time
    strhours, strminutes, meridian = time_match.captures
    hours = (meridian == &quot;AM&quot; ? strhours.to_i : strhours.to_i + 12)
    total_minutes = hours * 60 + strminutes.to_i + mins
    total_minutes = total_minutes % (24*60) # we only want the minutes that fit within a day
    adjusted_hours, adjusted_minutes = total_minutes.divmod(60)
    if adjusted_hours &gt; 12
        adjusted_hours -= 12
        meridian = &quot;PM&quot;
    else
        meridian = &quot;AM&quot;
    end
     
    &quot;%d:%02d %s&quot; % [adjusted_hours, adjusted_minutes, meridian]
end

Testing:
[ &quot;11:13 PM&quot;, 
  &quot;13:09 PM&quot;, #invalid!
  &quot;1:59 AM&quot;, 
  &quot;04:49 PM&quot;,
  &quot;4:79 PM&quot; #invalid!
].each do |time|
    begin
      puts flux_capacitor(time, 0)
    rescue 
      puts $!
    end
end
puts &quot;----------&quot;
puts &quot;#{flux_capacitor(&quot;11:13 PM&quot;, 10)}, expected: 11:23 PM&quot;
puts &quot;#{flux_capacitor(&quot;11:13 PM&quot;, 12*60)}, expected 11:13 AM&quot;
puts &quot;#{flux_capacitor(&quot;11:13 PM&quot;, 24*60)}, expected 11:13 PM&quot;
puts &quot;#{flux_capacitor(&quot;11:13 PM&quot;, 24*60 + 1)}, expected 11:14 PM&quot;
puts &quot;#{flux_capacitor(&quot;11:59 AM&quot;, 62)}, expected 1:01 PM&quot;
puts &quot;#{flux_capacitor(&quot;11:59 PM&quot;, 62)}, expected 1:01 AM&quot;