From: Justin Ethier Date: 2007-10-22T00:26:17+09:00 Subject: Re: [QUIZ] Time Window (#144) ------=_Part_1044_19209162.1192980378076 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Content-Disposition: inline Hello, Here is my solution to the quiz. First I used smaller classes to simplify TimeWindow: # Class to store a single time range defined by a start/end class TimeRange # Each Input in form of "HHMM" def initialize(start_str, end_str) @start = start_str.to_i @end = end_str.to_i end attr_reader :start, :end end # Represents a single time period for particular days and times # A time window may contain several of these frames class TimeFrame # Days - Bitmask of 7 fields (Sun @ 0, Mon @ 1, Tues @ 2, etc) # Time range - List of start/end time ranges defining the time frame def initialize(days, time_ranges) @days = days @time_ranges = time_ranges end # Does the given Time match this Time Frame? def include?(time) if @days[time.wday] # If no times then days matching is good enough return true if @time_ranges.size == 0 # Check time range(s) for time_range in @time_ranges time_n = time.hour * 100 + time.min return true if time_n >= time_range.start and time_n < time_range.end end end false end end The main class then simply parses the time window string at startup, saving each individual time window to memory. Then the include? method iterates through those time windows to determine if a particular time is in the window: # Defines a time window spanning multiple days and time ranges class TimeWindow Days = ["Sun", "Mon", "Tues", "Wed", "Thu", "Fri", "Sat"] # Constructor accepting a string as defined in ruby quiz description def initialize(time_window) @timeframes = [] for group in time_window.split(";") days, times = Array.new(7, false), [] for item in group.split(" ") # Range of values? if item.include?("-") # Yes, Figure out if range is days or times range = item.split("-") if Days.include?(range[0]) set_day_range(days, range[0], range[1]) else times << TimeRange.new(range[0], range[1]) end else days[Days.index(item)] = true if Days.include?(item) end end @timeframes << TimeFrame.new(days, times) end end # Set days in given range in the input array # Inputs: days - List of days in the time window # start_day, end_day - Day range to add to the window def set_day_range(days, start_day, end_day) pos = Days.index(start_day) while pos != (Days.index(end_day) + 1) % 7 days[pos] = true pos = (pos + 1) % 7 end end # Does the given Time match this time window? def include?(time) for time_frame in @timeframes return true if time_frame.include?(time) end return (@timeframes.size == 0) # Empty time string matches all times end private :set_day_range end Fortunately it passes all of the tests :) A pastie is available here: http://pastie.caboo.se/109346 Thanks, Justin On 10/19/07, Ruby Quiz wrote: > > 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: > > http://www.rubyquiz.com/ > > 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 > > ------=_Part_1044_19209162.1192980378076--