Skip to content

Instantly share code, notes, and snippets.

@rantler
Last active December 27, 2015 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rantler/7403845 to your computer and use it in GitHub Desktop.
Save rantler/7403845 to your computer and use it in GitHub Desktop.
Simple solution to time diff in clock face degrees. With tests.
class DegreeClock
HOURS_PER_DAY = 24
MINUTES_PER_HOUR = 60
DEGREES_PER_HOUR = 360
DEGREES_PER_DAY = DEGREES_PER_HOUR * HOURS_PER_DAY
DEGREES_PER_MINUTE = DEGREES_PER_HOUR / MINUTES_PER_HOUR
HOURS_PER_HALF_DAY = HOURS_PER_DAY / 2
class Time
attr_accessor :hours, :minutes, :meridian
def initialize(h, m, pm = nil)
@hours = h.to_i
@minutes = m.to_i
@meridian = (pm || :am).to_sym.downcase
end
def valid?
if hours >= HOURS_PER_DAY || hours < 0 || minutes >= MINUTES_PER_HOUR || minutes < 0
return false
end
true
end
def absolute_minutes
militarized_hours * MINUTES_PER_HOUR + minutes
end
def tomorrow?(earlier_time)
absolute_minutes < earlier_time.absolute_minutes
end
def militarized_hours
military_hours = hours
if meridian == :pm && hours != HOURS_PER_HALF_DAY
military_hours += HOURS_PER_HALF_DAY
end
military_hours == HOURS_PER_HALF_DAY && meridian == :am ? 0 : military_hours
end
def to_s
"%02d:%02d %s" % [hours, minutes, meridian]
end
end
def initialize(t1, t2)
@t1 = parse_time(t1)
@t2 = parse_time(t2)
end
def degrees_moved(commify = nil)
degrees = time_in_degrees(@t2, @t2.tomorrow?(@t1)) - time_in_degrees(@t1)
commify ? commify(degrees) : degrees
end
private
def parse_time(user_time)
raise 'Please provide a time in the format (H)H:MM (AM/PM)' unless user_time
elements = user_time.match(/(?<hours>\d{1,2}):(?<minutes>\d{1,2})\s?(?<meridian>am|pm)?/i)
validate_user_time(user_time, elements)
Time.new(*(elements.to_a.slice(1..3))).tap do |time|
raise "Invalid time '#{user_time}'" unless time.valid?
end
end
def time_in_degrees(time, tomorrow = nil)
degrees = (time.militarized_hours * DEGREES_PER_HOUR + time.minutes * DEGREES_PER_MINUTE)
if tomorrow
degrees += DEGREES_PER_DAY
end
degrees
end
def validate_user_time(user_time, elements)
unless elements && elements[:hours] && elements[:minutes]
raise "Unable to parse time '#{user_time}'"
end
end
def commify(number)
sign = number <=> 0
result = number.abs.to_s.reverse.chars.each_slice(3).map(&:join).join(',').reverse
sign < 0 ? "-#{result}" : result
end
end
require 'rspec'
require './degree_clock'
describe 'DegreeClock' do
subject { DegreeClock }
context 'degrees moved within the same day' do
it 'should be 900 degrees when moving from 10:15 AM to 12:45 PM' do
subject.new('10:15 am', '12:45 PM').degrees_moved.should eq(900)
end
it 'should be 360 degrees when moving from 11:00 AM to 12:00 PM' do
subject.new('11:00 am', '12:00 pm').degrees_moved.should eq(360)
end
it 'should be 4,314 degrees when moving from 12:00 PM to 11:59 PM' do
subject.new('12:00 pm', '11:59 pm').degrees_moved.should eq(4314)
end
it 'should be 0 degrees when moving from 12:00 PM to 12:00 PM' do
subject.new('12:00 pm', '12:00 pm').degrees_moved.should eq(0)
end
end
context 'degrees moved spanning into the next day' do
it 'should be 8,280 degrees when moving from 10:00 PM to 9:00 PM' do
subject.new('10:00 pm', '09:00 PM').degrees_moved.should eq(8280)
end
it 'should be 4,320 degrees when moving from 12:00 AM to 12:00 PM' do
subject.new('12:00 am', '12:00 PM').degrees_moved.should eq(4320)
end
it 'should be 3,966 degrees when moving from 12:59 PM to 12:00 AM' do
subject.new('12:59 pm', '12:00 AM').degrees_moved.should eq(3966)
end
it 'should be 8,634 degrees when moving from 12:01 PM to 12:00 PM' do
subject.new('12:01 pm', '12:00 PM').degrees_moved.should eq(8634)
end
it 'should be 8,634 degrees when moving from 12:00 AM to 11:59 PM' do
subject.new('12:00 am', '11:59 pm').degrees_moved.should eq(8634)
end
end
describe 'Time' do
subject { DegreeClock::Time }
context 'defaults' do
it 'should default to AM' do
subject.new(1, 2).meridian.should eq(:am)
end
it 'should honor AM / PM as symbols or strings' do
subject.new(1, 2, :am).meridian.should eq(:am)
subject.new(1, 2, :pm).meridian.should eq(:pm)
subject.new(1, 2, 'am').meridian.should eq(:am)
subject.new(1, 2, 'pm').meridian.should eq(:pm)
end
end
context 'validation' do
it 'should be valid when the hours are within range' do
subject.new(0, 1).valid?.should be_true
subject.new(1, 1).valid?.should be_true
subject.new(12, 1).valid?.should be_true
subject.new(23, 1).valid?.should be_true
end
it 'should be invalid when the hours are out of range' do
subject.new(-1, 1).valid?.should be_false
subject.new(24, 1).valid?.should be_false
subject.new(25, 1).valid?.should be_false
end
it 'should be valid when the minutes are with range' do
subject.new(1, 0).valid?.should be_true
subject.new(12, 1).valid?.should be_true
subject.new(1, 59).valid?.should be_true
end
it 'should be invalid when the minutes are out of range' do
subject.new(1, -1).valid?.should be_false
subject.new(1, 60).valid?.should be_false
subject.new(1, 61).valid?.should be_false
end
end
context 'tomorrow' do
it 'should be true when the first time is before than the second time' do
subject.new(9, 0, :pm).tomorrow?(subject.new(10, 0, :pm)).should be_true
subject.new(1, 0, :am).tomorrow?(subject.new(2, 0, :am)).should be_true
subject.new(12, 0, :am).tomorrow?(subject.new(12, 59, :pm)).should be_true
subject.new(2, 0, :pm).tomorrow?(subject.new(3, 0, :pm)).should be_true
end
it 'should be false when the first time is 11:59 PM and the second time is 12:00 PM' do
subject.new(11, 59, :pm).tomorrow?(subject.new(12, 00, :pm)).should be_false
end
end
context 'military time' do
it 'should return hours + 12 when the meridian is PM' do
subject.new(1, 0, :pm).militarized_hours.should eq(13)
end
it 'should not add 12 hours if it is AM and the time is not 12 AM' do
subject.new(3, 0, :am).militarized_hours.should eq(3)
end
it 'should return zero when it is 12:00 AM' do
subject.new(12, 0, :am).militarized_hours.should eq(0)
end
end
end
end
require './degree_clock'
print "Enter the first time: "
start_time = gets.chomp
print "Enter the second time: "
end_time = gets.chomp
clock = DegreeClock.new(start_time, end_time)
puts "Clock minute hand moved #{clock.degrees_moved(:commify)} degrees"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment