Here is my solution to the time window quiz. Range.create_from_string
is the workhorse method and it would be nice if that was refactored
into smaller pieces.
class TimeWindow
def initialize(definition_string)
@ranges = []
definition_string.split(/;/).each do |part|
@ranges << Range.create_from_string(part.strip)
end
@ranges << Range.create_from_string('') if @ranges.empty?
end
def include?(time)
@ranges.any? {|r| r.include?(time)}
end
class Range < Struct.new(:day_parts, :time_parts)
DAYS = %w{Sun Mon Tue Wed Thu Fri Sat}
def self.create_from_string(str)
time_parts = []
day_parts = []
str.split(/ /).each do |token|
token.strip!
if DAYS.include?(token)
day_parts << token
elsif token =~ /^(\w{3})-(\w{3})$/
start_day, end_day = $1, $2
start_found = false
(DAYS * 2).each do |d|
start_found = true if d == start_day
day_parts << d if start_found
break if d == end_day && start_found
end
elsif token =~ /^(\d{4})-(\d{4})$/
time_parts << (($1.to_i)..($2.to_i - 1))
else
raise "Unrecognized token: #{token}"
end
end
time_parts << (0..2399) if time_parts.empty?
day_parts = DAYS.clone if day_parts.empty?
self.new(day_parts, time_parts)
end
def include?(time)
matches_day?(time) && matches_minute?(time)
end
def matches_day?(time)
day = time.strftime('%a')
self.day_parts.include?(day)
end
def matches_minute?(time)
minute = time.strftime('%H%M').to_i
self.time_parts.any? {|tp| tp.include?(minute) }
end
end
end