Re: Time Window (#144)

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 Q. [email protected]
To: ruby-talk ML [email protected]
Sent: Friday, October 19, 2007 7:14:00 AM
Subject: [QUIZ] Time Window (#144)

The three rules of Ruby Q.:

  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 Q. by submitting ideas as often as you can:

http://www.rubyquiz.com/

  1. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps
everyone
on Ruby T. follow the discussion. Please reply to the original quiz
message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Brian C.

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

On 10/24/07, James K. [email protected] wrote:

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?.

class Array
def some
each {|el| return true if yield el}
false
end
end

Isn’t this the same as Enumerable#any?

Jesus.

That’s the same thought that I had. Assuming that any? didn’t exist,
I am still fearful of adding methods to core classes. I think core
classes should only be modified in special cases, like building a
framework.

How do the rest of us feel about this?

  • Philip

On Oct 24, 2007, at 2:16 AM, Jesús Gabriel y Galán wrote:

false

end
end

Isn’t this the same as Enumerable#any?

It sure is.

James Edward G. II

On Oct 24, 2007, at 3:04 AM, Philip G. wrote:

That’s the same thought that I had. Assuming that any? didn’t
exist, I am still fearful of adding methods to core classes. I
think core classes should only be modified in special cases, like
building a framework.

How do the rest of us feel about this?

Like most things, it’s just another tool in your belt. You’ll need
to consider when to use it and when not to.

On the plus side, slowly transforming Ruby into the language of a
problem is a powerful way to work. The minus is that you can make it
hard to use your code with other Ruby solutions in the process.

You will need to decide when that’s a good trade-off and when it’s
not. Ruby trusts you to make the choice.

James Edward G. II

On 10/24/07, Philip G. [email protected] wrote:

That’s the same thought that I had. Assuming that any? didn’t exist,
I am still fearful of adding methods to core classes. I think core
classes should only be modified in special cases, like building a
framework.

How do the rest of us feel about this?

Well, first of all it depends if you are building a library that
someone will use, or just a program that will run independently. If
it’s the former, even then I would think that just adding methods is
OK, as long as the method makes sense. What should be done with really
great care (or not at all) is modifying existing methods. But I’m
fairly new to Ruby so take my opinion with a grain of salt.

Jesus.

On Oct 24, 2007, at 9:10 AM, Ken B. wrote:

$days = {“Sun”=>0,
“Mon”=>1,
“Tue”=>2,
“Wed”=>3,
“Thu”=>4,
“Fri”=>5,
“Sat”=>6}

I suggest using a constant rather than a global variable for this. You
can put the constant in the TimeWindow class.

This is also just an Array disguised as a Hash.

James Edward G. II

On Wed, 24 Oct 2007 10:04:56 +0900, James K. wrote:

          "Wed"=>3,
          "Thu"=>4,
          "Fri"=>5,
          "Sat"=>6}

I suggest using a constant rather than a global variable for this. You
can put the constant in the TimeWindow class.

class Array
def some
each {|el| return true if yield el}
false
end
end

This has already been discussed as being equal to Enumerable#any?

class TimeWindow
def initialize(win_str)
@times = ([nil]*7).map{[]}

This is the same as Array.new(7){[]}, which is a more usual way to
express this.

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

(’|’)})/)

  end
  @times[time.wday].some{|trange| trange ===
  (time.hour*100+time.min)}
end

end
end

Looks generally like my solution.

–Ken

On Oct 24, 9:16 am, James Edward G. II [email protected]
wrote:

subarray contains all the ranges for a given day. I then wrote the
“Sat”=>6}

I suggest using a constant rather than a global variable for this. You
can put the constant in the TimeWindow class.

This is also just an Array disguised as a Hash.

James Edward G. II

It could be a lot worse. Which of us hasn’t run across something like
this?

days = {
0 => ‘Sun’,
1 => ‘Mon’,
2 => ‘Tue’,
3 => ‘Wed’,
4 => ‘Thu’,
5 => ‘Fri’,
6 => ‘Sat’,
}