Last active
December 27, 2015 23:09
-
-
Save rantler/7403845 to your computer and use it in GitHub Desktop.
Simple solution to time diff in clock face degrees. With tests.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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