Here's my solution. It converts a time window into an array of arrays of ranges. Each range matches an interval of time (e.g.: 500..1700); each subarray contains all the ranges for a given day. I then wrote the simple Array#some method that returns true if the given predicate returns true for at least one of its elements, which I used to write TimeWindow#incude?.
$days = {"Sun"=>0,
"Mon"=>1,
"Tue"=>2,
"Wed"=>3,
"Thu"=>4,
"Fri"=>5,
"Sat"=>6}
class Array
def some
each {|el| return true if yield el}
false
end
end
class TimeWindow
def initialize(win_str)
@times = ([nil]*7).map{[]}
win_str << " " #In case of empty
win_str.split(/;/).each do |win|
days_str = win.match(/(((#{$days.keys.join('|')}|)( |-)?)*)/)[0]..strip
days = []
days_str.scan(/#{$days.keys.join('|')}/) do |day|
days << $days[day]
end
days_str.scan(/(#{$days.keys.join('|')})-(#{$days.keys.join('|')})/) do
a = $days[$1]
b = $days[$2]
days += (a..(b > a ? b : b+7))..to_a.map{|d|d%7}
end
days = (0..6).to_a if days.empty?
times = []
win.scan(/(\d{4})-(\d{4})/) do
times << (($1.to_i)...($2.to_i))
end
times = [0..2400] if times.empty?
days.each do |d|
times.each do |t|
@times[d] << t
end
end
end
def include?(time)
@times[time.wday].some{|trange| trange === (time.hour*100+time.min)}
end
end
end
···
----- Original Message ----
From: Ruby Quiz <james@grayproductions.net>
To: ruby-talk ML <ruby-talk@ruby-lang.org>
Sent: Friday, October 19, 2007 7:14:00 AM
Subject: [QUIZ] Time Window (#144)
The three rules of Ruby Quiz:
1. Please do not post any solutions or spoiler discussion for this
quiz until
48 hours have passed from the time on this message.
2.. Support Ruby Quiz by submitting ideas as often as you can:
3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby Talk follow the discussion. Please reply to the original quiz
message,
if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
by Brian Candler
Write a Ruby class which can tell you whether the current time (or any
given
time) is within a particular "time window". Time windows are defined by
strings
in the following format:
# 0700-0900 # every day between these times
# Sat Sun # all day Sat and Sun, no other
times
# Sat Sun 0700-0900 # 0700-0900 on Sat and Sun only
# Mon-Fri 0700-0900 # 0700-0900 on Monday to Friday
only
# Mon-Fri 0700-0900; Sat Sun # ditto plus all day Sat and Sun
# Fri-Mon 0700-0900 # 0700-0900 on Fri Sat Sun Mon
# Sat 0700-0800; Sun 0800-0900 # 0700-0800 on Sat, plus
0800-0900 on Sun
Time ranges should exclude the upper bound, i.e. 0700-0900 is 07:00:00
to
08:59:59. An empty time window means "all times everyday". Here are
some test
cases to make it clearer:
class TestTimeWindow < Test::Unit::TestCase
def test_window_1
w = TimeWindow.new("Sat-Sun; Mon Wed 0700-0900; Thu 0700-0900
1000-1200")
assert ! w.include?(Time.mktime(2007,9,25,8,0,0)) # Tue
assert w.include?(Time.mktime(2007,9,26,8,0,0)) # Wed
assert ! w.include?(Time.mktime(2007,9,26,11,0,0))
assert ! w.include?(Time.mktime(2007,9,27,6,59,59)) # Thu
assert w.include?(Time.mktime(2007,9,27,7,0,0))
assert w.include?(Time.mktime(2007,9,27,8,59,59))
assert ! w.include?(Time.mktime(2007,9,27,9,0,0))
assert w.include?(Time.mktime(2007,9,27,11,0,0))
assert w.include?(Time.mktime(2007,9,29,11,0,0)) # Sat
assert w.include?(Time.mktime(2007,9,29,0,0,0))
assert w.include?(Time.mktime(2007,9,29,23,59,59))
end
def test_window_2
w = TimeWindow.new("Fri-Mon")
assert ! w.include?(Time.mktime(2007,9,27)) # Thu
assert w.include?(Time.mktime(2007,9,28))
assert w.include?(Time.mktime(2007,9,29))
assert w.include?(Time.mktime(2007,9,30))
assert w.include?(Time.mktime(2007,10,1))
assert ! w.include?(Time.mktime(2007,10,2)) # Tue
end
def test_window_nil
w = RDS::TimeWindow.new("")
assert w.include?(Time.mktime(2007,9,25,1,2,3)) # all times
end
end
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com