Class: TimePoint

Inherits:
Object show all
Defined in:
lib/time_point.rb

Defined Under Namespace

Classes: ArrayOfRanges, Classification, Month, Union, WDay

Constant Summary collapse

VERSION =
'1.0.0'
CommonPatterns =

These are in a specific order

[
  'ord range ord',
  'ord union ord',
  'wday range wday',
  'wday union wday',
  'month ord',
  'ord wday',
  'ord month timerange',
  'month_ord timerange',
  'month union month',
  'month range month',
  'ord_wday month',
  'ord_wday timerange',
  'ord_wday_month timerange'
]
CommonPatternActions =
{
  'ord range ord' => lambda {|words,i|
    words[i][:ord] = (words[i][:ord].to_i..words[i+2][:ord].to_i)
    words.slice!(i+1,2)
  },
  'ord union ord' => lambda {|words,i|
    words[i][:ord] = ArrayOfRanges.new(words[i][:ord], words[i+2][:ord])
    words.slice!(i+1,2)
  },
  'wday range wday' => lambda {|words,i|
    words[i][:wday] = (words[i][:wday].to_i..words[i+2][:wday].to_i)
    words.slice!(i+1,2)
  },
  'wday union wday' => lambda {|words,i|
    words[i][:wday] = ArrayOfRanges.new(words[i][:wday], words[i+2][:wday])
    words.slice!(i+1,2)
  },
  'month ord' => lambda {|words,i|
    words[i][:type] = 'month_ord'
    words[i][:ord] = words[i+1][:ord]
    words.slice!(i+1,1)
  },
  'ord wday' => lambda {|words,i|
    words[i][:type] = 'ord_wday'
    words[i][:wday] = words[i+1][:wday]
    words.slice!(i+1,1)
  },
  'ord month timerange' => lambda {|words,i|
    words[i][:type] = 'month_ord_timerange'
    words[i][:month] = words[i+1][:month]
    words[i][:start_time] = words[i+2][:start_time]
    words[i][:end_time] = words[i+2][:end_time]
    words.slice!(i+1,2)
  },
  'month_ord timerange' => lambda {|words,i|
    words[i][:type] = 'month_ord_timerange'
    words[i][:start_time] = words[i+1][:start_time]
    words[i][:end_time] = words[i+1][:end_time]
    words.slice!(i+1,1)
  },
  'month union month' => lambda {|words,i|
    words[i][:month] = ArrayOfRanges.new(words[i][:month], words[i+2][:month])
    words.slice!(i+1,2)
  },
  'month range month' => lambda {|words,i|
    raise "Not Implemented Yet!"
  },
  'ord_wday month' => lambda {|words,i|
    words[i][:type] = 'ord_wday_month'
    words[i][:month] = words[i+1][:month]
    words.slice!(i+1,1)
  },
  'ord_wday timerange' => lambda {|words,i|
    words[i][:type] = 'ord_wday_timerange'
    words[i][:start_time] = words[i+1][:start_time]
    words[i][:end_time] = words[i+1][:end_time]
    words.slice!(i+1,1)
  },
  'ord_wday_month timerange' => lambda {|words,i|
    words[i][:type] = 'ord_wday_month_timerange'
    words[i][:start_time] = words[i+1][:start_time]
    words[i][:end_time] = words[i+1][:end_time]
    words.slice!(i+1,1)
  }
}
BooleanPatterns =
[
  'union'
]
BooleanPatternActions =
{
  'union' => lambda {|words,i|
    words[i-1] = TimePoint::Union.new(words[i-1], words[i+1])
    words.slice!(i,2)
    puts words.inspect if $DEBUG
  }
}
TimeRegexp =
'\d{1,2}(?::\d{1,2})?(?:am|pm)'
WordTypes =
{
  :ord => /^(\d+)(?:st|nd|rd|th)?$/i,
  :wday => /^(#{WDay.order.join('|')})s$/i,
  :month => /^#{Month.order.join('|')}$/i,
  :union => /^(?:and)$/i,
  :range => /^(?:-|to|through)$/,
  :timerange => /^(#{TimeRegexp}?)-(#{TimeRegexp})$/i,
  :time => /^#{TimeRegexp}$/i
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ TimePoint

Returns a new instance of TimePoint.



294
295
296
297
298
# File 'lib/time_point.rb', line 294

def initialize(options)
  options.each do |key,value|
    instance_variable_set(:"@#{key}", value)
  end
end

Class Method Details

.parse(expression) ⇒ Object



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/time_point.rb', line 226

def parse(expression)
  # 1. Normalize the expression
  # TODO: re-create normalize: ' -&| ', 'time-time'
  expression.gsub!(/([\-\&\|])/,' \1 ')
  expression.gsub!(/(#{TimeRegexp}?)\s+-\s+(#{TimeRegexp})/,'\1-\2')

  # 2. Analyze the expression
  words = expression.split(/\s+/)
  puts words.inspect if $DEBUG
  analyzed_expression = words.inject([]) do |a,word|
    a << case word
    when WordTypes[:ord]
      {:type => 'ord', :ord => $1}
    when WordTypes[:wday]
      {:type => 'wday', :wday => $1}
    when WordTypes[:month]
      {:type => 'month', :month => word}
    when WordTypes[:union]
      {:type => 'union'}
    when WordTypes[:range]
      {:type => 'range'}
    when WordTypes[:timerange]
      {:type => 'timerange', :start_time => $1, :end_time => $2}
    when WordTypes[:time]
      {:type => 'time', :time => word}
    end
  end.compact
  def analyzed_expression.collect_types
    collect {|e| e[:type]}
  end

  # 3. Combine common patterns
  puts analyzed_expression.inspect if $DEBUG
  puts analyzed_expression.collect_types.inspect if $DEBUG

  something_was_modified = true
  while something_was_modified
    something_was_modified = false
    before_length = analyzed_expression.length
    CommonPatterns.each do |pattern|
      while i = analyzed_expression.collect_types.includes_sequence?(pattern.split(/ /))
        CommonPatternActions[pattern].call(analyzed_expression,i)
      end
    end
    after_length = analyzed_expression.length
    something_was_modified = true if before_length != after_length
  end
  
  puts analyzed_expression.inspect if $DEBUG
  puts analyzed_expression.collect_types.inspect if $DEBUG

  # What remains should be simply sections of boolean logic
  # 4. Parse boolean logic
  analyzed_expression.each_index do |i|
    analyzed_expression[i] = TimePoint.new(analyzed_expression[i]) unless ['union', 'range'].include? analyzed_expression[i][:type]
  end

  BooleanPatterns.each do |pattern|
    while i = analyzed_expression.collect_types.includes_sequence?(pattern.split(/ /))
      BooleanPatternActions[pattern].call(analyzed_expression,i)
      break if analyzed_expression.length == 1
    end
  end

  return analyzed_expression[0]
end

Instance Method Details

#[](key) ⇒ Object



300
301
302
# File 'lib/time_point.rb', line 300

def [](key)
  instance_variable_get(:"@#{key}")
end

#end_time(date = nil) ⇒ Object



364
365
366
367
368
369
370
# File 'lib/time_point.rb', line 364

def end_time(date=nil)
  if date
    Time.parse("#{date.strftime("%Y-%m-%d")} #{@end_time}")
  else
    @end_time
  end
end

#include?(datetime) ⇒ Boolean

Returns:

  • (Boolean)


312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/time_point.rb', line 312

def include?(datetime)
  return false unless occurs_on_day?(datetime)
  if @type =~ /timerange/
    test_date = datetime.strftime("%Y-%m-%d")
    test_start_time = Time.parse("#{test_date} #{@start_time}#{start_pm? ? 'pm' : 'am'}")
    test_end_time = Time.parse("#{test_date} #{@end_time}")
    puts "TimeRange: date:#{test_date} test_start:#{test_start_time} test_end:#{test_end_time} <=> #{datetime}" if $DEBUG
    return false unless datetime.between?(test_start_time, test_end_time)
  end
  return true
  puts "#{datetime} Included!" if $DEBUG
end

#occurrances_on_day(date) ⇒ Object



353
354
355
# File 'lib/time_point.rb', line 353

def occurrances_on_day(date)
  occurs_on_day?(date) ? [{:start_time => start_time(date), :end_time => end_time(date)}] : []
end

#occurs_on_day?(datetime) ⇒ Boolean

Returns:

  • (Boolean)


325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/time_point.rb', line 325

def occurs_on_day?(datetime)
  puts "#{datetime} IN? #{inspect}" if $DEBUG
  puts "Correct month? #{Month.order[datetime.month-1].inspect}==#{@month.inspect} : #{Month.order[datetime.month-1].value_in?(@month)}" if @type =~ /month/ if $DEBUG
  return false if @type =~ /month/ && !Month.order[datetime.month-1].value_in?(@month)
  if @type =~ /ord_wday/
    puts "Weekday: #{WDay.order[datetime.wday].inspect} in? #{@wday.inspect} == #{WDay.order[datetime.wday].value_in?(@wday)}" if $DEBUG
    return false unless WDay.order[datetime.wday].value_in?(@wday)
    puts "WeekdayOrd: #{datetime.wday_ord} in? #{@ord.inspect} == #{datetime.wday_ord.value_in?(@ord)}" if $DEBUG
    return false unless datetime.wday_ord.value_in?(@ord)
  end
  if @type =~ /month_ord/
    puts "Day #{datetime.day} == #{@ord.inspect} >> #{datetime.day.value_in?(@ord)}" if $DEBUG
    return false unless datetime.day.value_in?(@ord)
  end
  # puts "Type: #{@type}" if $DEBUG
  # case
  # when @type == 'ord_wday_month'
  #   return true
  # when @type == 'ord_wday_month_timerange'
  #   return true
  # when @type == 'ord_wday_timerange'
  #   return true
  # when @type == 'month_ord_timerange'
  # end
  puts "Occurs on #{datetime}!" if $DEBUG
  return true
end

#start_pm?Boolean

Returns:

  • (Boolean)


304
305
306
307
308
309
310
# File 'lib/time_point.rb', line 304

def start_pm?
  if @start_time =~ /(am|pm)$/ || @end_time =~ /(am|pm)$/
    $1 == 'pm'
  else
    nil
  end
end

#start_time(date = nil) ⇒ Object



357
358
359
360
361
362
363
# File 'lib/time_point.rb', line 357

def start_time(date=nil)
  if date
    Time.parse("#{date.strftime("%Y-%m-%d")} #{@start_time}#{start_pm? ? 'pm' : 'am'}")
  else
    @start_time
  end
end