On Fri, 19 Oct 2007 21:14:00 +0900, 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

#!/usr/bin/env ruby

class TimeWindow
  DAYNAMES=%w[Sun Mon Tue Wed Thu Fri Sat]
  DAYNAME=%r{Sun|Mon|Tue|Wed|Thu|Fri|Sat}
  TIME=%r{[0-9]+}

  def initialize string
    string = " " if string == "" #make an empty string match everythingworking around the way clauses are split
    #splitting an empty string gives an empty array (i.e. no clauses)
    #splitting a " " gives a single clause with no day names (so all are used) and no times (so all are used)
    @myarray=Array.new(7){[]}
    
    #different clauses are split by semicolons
    string.split(/\s*;\s*/).each do |clause|

      #find the days that this clause applies to
      curdays=[]
      clause.scan(/(#{DAYNAME})(?:(?=\s)|$)|(#{DAYNAME})-(#{DAYNAME})/) do |single,start,finish|
        single &&= DAYNAMES.index(single)
        start &&= DAYNAMES.index(start)
        finish &&= DAYNAMES.index(finish)
        curdays << single if single
        if start and finish
          (start..finish).each{|x| curdays << x} if start<finish
          (start..6).each{|x| curdays << x} if finish<start
          (0..finish).each{|x| curdays << x} if finish<start
        end
      end

      #all days if no day names were given
      curdays=(0..6).to_a if curdays==[]


      #find the times that this clause applies to
      found=false
      clause.scan(/(#{TIME})-(#{TIME})/) do |start,finish|
        found=true
        curdays.each do |day|
          @myarray[day] << [start,finish]
        end
      end

      #all times if none were given
      if not found
        curdays.each {|day| @myarray[day] << ["0000","2400"]}
      end
    end
  end

  def include? time
    matchday=time.wday
    matchtime="%02d%02d" % [time.hour,time.min]
    @myarray[matchday].any?{|start,finish| start<=matchtime && matchtime<finish}
  end
  
  alias_method :===, :include?

end




-- 
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/