module Chronic::Handlers

Public Instance Methods

day_or_time(day_start, time_tokens, options) click to toggle source

support methods

# File lib/chronic/handlers.rb, line 495
def day_or_time(day_start, time_tokens, options)
  outer_span = Span.new(day_start, day_start + (24 * 60 * 60))

  unless time_tokens.empty?
    self.now = outer_span.begin
    get_anchor(dealias_and_disambiguate_times(time_tokens, options), options.merge(:context => :future))
  else
    outer_span
  end
end
dealias_and_disambiguate_times(tokens, options) click to toggle source
# File lib/chronic/handlers.rb, line 586
def dealias_and_disambiguate_times(tokens, options)
  # handle aliases of am/pm
  # 5:00 in the morning -> 5:00 am
  # 7:00 in the evening -> 7:00 pm

  day_portion_index = nil
  tokens.each_with_index do |t, i|
    if t.get_tag(RepeaterDayPortion)
      day_portion_index = i
      break
    end
  end

  time_index = nil
  tokens.each_with_index do |t, i|
    if t.get_tag(RepeaterTime)
      time_index = i
      break
    end
  end

  if day_portion_index && time_index
    t1 = tokens[day_portion_index]
    t1tag = t1.get_tag(RepeaterDayPortion)

    case t1tag.type
    when :morning
      puts '--morning->am' if Chronic.debug
      t1.untag(RepeaterDayPortion)
      t1.tag(RepeaterDayPortion.new(:am))
    when :afternoon, :evening, :night
      puts "--#{t1tag.type}->pm" if Chronic.debug
      t1.untag(RepeaterDayPortion)
      t1.tag(RepeaterDayPortion.new(:pm))
    end
  end

  # handle ambiguous times if :ambiguous_time_range is specified
  if options[:ambiguous_time_range] != :none
    ambiguous_tokens = []

    tokens.each_with_index do |token, i|
      ambiguous_tokens << token
      next_token = tokens[i + 1]

      if token.get_tag(RepeaterTime) && token.get_tag(RepeaterTime).type.ambiguous? && (!next_token || !next_token.get_tag(RepeaterDayPortion))
        distoken = Token.new('disambiguator')

        distoken.tag(RepeaterDayPortion.new(options[:ambiguous_time_range]))
        ambiguous_tokens << distoken
      end
    end

    tokens = ambiguous_tokens
  end

  tokens
end
find_within(tags, span, pointer) click to toggle source

Recursively finds repeaters within other repeaters. Returns a Span representing the innermost time span or nil if no repeater union could be found

# File lib/chronic/handlers.rb, line 559
def find_within(tags, span, pointer)
  puts "--#{span}" if Chronic.debug
  return span if tags.empty?

  head = tags.shift
  head.start = (pointer == :future ? span.begin : span.end)
  h = head.this(:none)

  if span.cover?(h.begin) || span.cover?(h.end)
    find_within(tags, h, pointer)
  end
end
get_anchor(tokens, options) click to toggle source
# File lib/chronic/handlers.rb, line 506
def get_anchor(tokens, options)
  grabber = Grabber.new(:this)
  pointer = :future
  repeaters = get_repeaters(tokens)
  repeaters.size.times { tokens.pop }

  if tokens.first && tokens.first.get_tag(Grabber)
    grabber = tokens.shift.get_tag(Grabber)
  end

  head = repeaters.shift
  head.start = self.now

  case grabber.type
  when :last
    outer_span = head.next(:past)
  when :this
    if options[:context] != :past and repeaters.size > 0
      outer_span = head.this(:none)
    else
      outer_span = head.this(options[:context])
    end
  when :next
    outer_span = head.next(:future)
  else
    raise "Invalid grabber"
  end

  if Chronic.debug
    puts "Handler-class: #{head.class}"
    puts "--#{outer_span}"
  end

  find_within(repeaters, outer_span, pointer)
end
get_repeaters(tokens) click to toggle source
# File lib/chronic/handlers.rb, line 542
def get_repeaters(tokens)
  tokens.map { |token| token.get_tag(Repeater) }.compact.sort.reverse
end
handle_generic(tokens, options) click to toggle source

Handle generic timestamp (ruby 1.8)

# File lib/chronic/handlers.rb, line 134
def handle_generic(tokens, options)
  t = Chronic.time_class.parse(options[:text])
  Span.new(t, t + 1)
rescue ArgumentError => e
  raise e unless e.message =~ /out of range/
end
handle_m_d(month, day, time_tokens, options) click to toggle source

Handle month/day

# File lib/chronic/handlers.rb, line 6
def handle_m_d(month, day, time_tokens, options)
  month.start = self.now
  span = month.this(options[:context])
  year, month = span.begin.year, span.begin.month
  day_start = Chronic.time_class.local(year, month, day)

  day_or_time(day_start, time_tokens, options)
end
handle_o_r_g_r(tokens, options) click to toggle source

Handle ordinal/repeater/grabber/repeater

# File lib/chronic/handlers.rb, line 488
def handle_o_r_g_r(tokens, options)
  outer_span = get_anchor(tokens[2..3], options)
  handle_orr(tokens[0..1], outer_span, options)
end
handle_o_r_s_r(tokens, options) click to toggle source

Handle ordinal/repeater/separator/repeater

# File lib/chronic/handlers.rb, line 482
def handle_o_r_s_r(tokens, options)
  outer_span = get_anchor([tokens[3]], options)
  handle_orr(tokens[0..1], outer_span, options)
end
handle_od_rm(tokens, options) click to toggle source

Handle ordinal this month

# File lib/chronic/handlers.rb, line 53
def handle_od_rm(tokens, options)
  day = tokens[0].get_tag(OrdinalDay).type
  month = tokens[2].get_tag(RepeaterMonth)
  handle_m_d(month, day, tokens[3..tokens.size], options)
end
handle_od_rmn(tokens, options) click to toggle source

Handle ordinal-day/repeater-month-name

# File lib/chronic/handlers.rb, line 60
def handle_od_rmn(tokens, options)
  month = tokens[1].get_tag(RepeaterMonthName)
  day = tokens[0].get_tag(OrdinalDay).type

  return if month_overflow?(self.now.year, month.index, day)

  handle_m_d(month, day, tokens[2..tokens.size], options)
end
handle_od_rmn_sy(tokens, options) click to toggle source

Handle oridinal-day/repeater-month-name/scalar-year

# File lib/chronic/handlers.rb, line 176
def handle_od_rmn_sy(tokens, options)
  day = tokens[0].get_tag(OrdinalDay).type
  month = tokens[1].get_tag(RepeaterMonthName).index
  year = tokens[2].get_tag(ScalarYear).type
  time_tokens = tokens.last(tokens.size - 3)

  return if month_overflow?(year, month, day)

  begin
    day_start = Chronic.time_class.local(year, month, day)
    day_or_time(day_start, time_tokens, options)
  rescue ArgumentError
    nil
  end
end
handle_orr(tokens, outer_span, options) click to toggle source

Handle oridinal repeaters

# File lib/chronic/handlers.rb, line 463
def handle_orr(tokens, outer_span, options)
  repeater = tokens[1].get_tag(Repeater)
  repeater.start = outer_span.begin - 1
  ordinal = tokens[0].get_tag(Ordinal).type
  span = nil

  ordinal.times do
    span = repeater.next(:future)

    if span.begin >= outer_span.end
      span = nil
      break
    end
  end

  span
end
handle_p_s_r(tokens, options) click to toggle source

Handle pointer/scalar/repeater

# File lib/chronic/handlers.rb, line 442
def handle_p_s_r(tokens, options)
  new_tokens = [tokens[1], tokens[2], tokens[0]]
  handle_s_r_p(new_tokens, options)
end
handle_r(tokens, options) click to toggle source

Handle repeaters

# File lib/chronic/handlers.rb, line 412
def handle_r(tokens, options)
  dd_tokens = dealias_and_disambiguate_times(tokens, options)
  get_anchor(dd_tokens, options)
end
handle_r_g_r(tokens, options) click to toggle source

Handle repeater/grabber/repeater

# File lib/chronic/handlers.rb, line 418
def handle_r_g_r(tokens, options)
  new_tokens = [tokens[1], tokens[0], tokens[2]]
  handle_r(new_tokens, options)
end
handle_rdn_od(tokens, options) click to toggle source

Handle RepeaterDayName OrdinalDay

# File lib/chronic/handlers.rb, line 327
def handle_rdn_od(tokens, options)
  day = tokens[1].get_tag(OrdinalDay).type
  time_tokens = tokens.last(tokens.size - 2)
  year = self.now.year
  month = self.now.month
  if options[:context] == :future
    self.now.day > day ? month += 1 : month
  end

  return if month_overflow?(year, month, day)

  begin
    if time_tokens.empty?
      start_time = Chronic.time_class.local(year, month, day)
      end_time = time_with_rollover(year, month, day + 1)
      Span.new(start_time, end_time)
    else
      day_start = Chronic.time_class.local(year, month, day)
      day_or_time(day_start, time_tokens, options)
    end
  rescue ArgumentError
    nil
  end
end
handle_rdn_rmn_od(tokens, options) click to toggle source

Handle RepeaterDayName RepeaterMonthName OrdinalDay

# File lib/chronic/handlers.rb, line 287
def handle_rdn_rmn_od(tokens, options)
  month = tokens[1].get_tag(RepeaterMonthName)
  day = tokens[2].get_tag(OrdinalDay).type
  time_tokens = tokens.last(tokens.size - 3)
  year = self.now.year

  return if month_overflow?(year, month.index, day)

  begin
    if time_tokens.empty?
      start_time = Chronic.time_class.local(year, month.index, day)
      end_time = time_with_rollover(year, month.index, day + 1)
      Span.new(start_time, end_time)
    else
      day_start = Chronic.time_class.local(year, month.index, day)
      day_or_time(day_start, time_tokens, options)
    end
  rescue ArgumentError
    nil
  end
end
handle_rdn_rmn_od_sy(tokens, options) click to toggle source

Handle RepeaterDayName RepeaterMonthName OrdinalDay ScalarYear

# File lib/chronic/handlers.rb, line 310
def handle_rdn_rmn_od_sy(tokens, options)
  month = tokens[1].get_tag(RepeaterMonthName)
  day = tokens[2].get_tag(OrdinalDay).type
  year = tokens[3].get_tag(ScalarYear).type

  return if month_overflow?(year, month.index, day)

  begin
    start_time = Chronic.time_class.local(year, month.index, day)
    end_time = time_with_rollover(year, month.index, day + 1)
    Span.new(start_time, end_time)
  rescue ArgumentError
    nil
  end
end
handle_rdn_rmn_sd(tokens, options) click to toggle source

Handle RepeaterDayName RepeaterMonthName ScalarDay

# File lib/chronic/handlers.rb, line 353
def handle_rdn_rmn_sd(tokens, options)
  month = tokens[1].get_tag(RepeaterMonthName)
  day = tokens[2].get_tag(ScalarDay).type
  time_tokens = tokens.last(tokens.size - 3)
  year = self.now.year

  return if month_overflow?(year, month.index, day)

  begin
    if time_tokens.empty?
      start_time = Chronic.time_class.local(year, month.index, day)
      end_time = time_with_rollover(year, month.index, day + 1)
      Span.new(start_time, end_time)
    else
      day_start = Chronic.time_class.local(year, month.index, day)
      day_or_time(day_start, time_tokens, options)
    end
  rescue ArgumentError
    nil
  end
end
handle_rdn_rmn_sd_sy(tokens, options) click to toggle source

Handle RepeaterDayName RepeaterMonthName ScalarDay ScalarYear

# File lib/chronic/handlers.rb, line 376
def handle_rdn_rmn_sd_sy(tokens, options)
  month = tokens[1].get_tag(RepeaterMonthName)
  day = tokens[2].get_tag(ScalarDay).type
  year = tokens[3].get_tag(ScalarYear).type

  return if month_overflow?(year, month.index, day)

  begin
    start_time = Chronic.time_class.local(year, month.index, day)
    end_time = time_with_rollover(year, month.index, day + 1)
    Span.new(start_time, end_time)
  rescue ArgumentError
    nil
  end
end
handle_rmn_od(tokens, options) click to toggle source

Handle repeater-month-name/ordinal-day

# File lib/chronic/handlers.rb, line 43
def handle_rmn_od(tokens, options)
  month = tokens[0].get_tag(RepeaterMonthName)
  day = tokens[1].get_tag(OrdinalDay).type

  return if month_overflow?(self.now.year, month.index, day)

  handle_m_d(month, day, tokens[2..tokens.size], options)
end
handle_rmn_od_on(tokens, options) click to toggle source

Handle repeater-month-name/ordinal-day with separator-on

# File lib/chronic/handlers.rb, line 96
def handle_rmn_od_on(tokens, options)
  if tokens.size > 3
    month = tokens[2].get_tag(RepeaterMonthName)
    day = tokens[3].get_tag(OrdinalDay).type
    token_range = 0..1
  else
    month = tokens[1].get_tag(RepeaterMonthName)
    day = tokens[2].get_tag(OrdinalDay).type
    token_range = 0..0
  end

  return if month_overflow?(self.now.year, month.index, day)

  handle_m_d(month, day, tokens[token_range], options)
end
handle_rmn_od_sy(tokens, options) click to toggle source

Handle repeater-month-name/ordinal-day/scalar-year

# File lib/chronic/handlers.rb, line 159
def handle_rmn_od_sy(tokens, options)
  month = tokens[0].get_tag(RepeaterMonthName).index
  day = tokens[1].get_tag(OrdinalDay).type
  year = tokens[2].get_tag(ScalarYear).type
  time_tokens = tokens.last(tokens.size - 3)

  return if month_overflow?(year, month, day)

  begin
    day_start = Chronic.time_class.local(year, month, day)
    day_or_time(day_start, time_tokens, options)
  rescue ArgumentError
    nil
  end
end
handle_rmn_sd(tokens, options) click to toggle source

Handle repeater-month-name/scalar-day

# File lib/chronic/handlers.rb, line 16
def handle_rmn_sd(tokens, options)
  month = tokens[0].get_tag(RepeaterMonthName)
  day = tokens[1].get_tag(ScalarDay).type

  return if month_overflow?(self.now.year, month.index, day)

  handle_m_d(month, day, tokens[2..tokens.size], options)
end
handle_rmn_sd_on(tokens, options) click to toggle source

Handle repeater-month-name/scalar-day with separator-on

# File lib/chronic/handlers.rb, line 26
def handle_rmn_sd_on(tokens, options)
  if tokens.size > 3
    month = tokens[2].get_tag(RepeaterMonthName)
    day = tokens[3].get_tag(ScalarDay).type
    token_range = 0..1
  else
    month = tokens[1].get_tag(RepeaterMonthName)
    day = tokens[2].get_tag(ScalarDay).type
    token_range = 0..0
  end

  return if month_overflow?(self.now.year, month.index, day)

  handle_m_d(month, day, tokens[token_range], options)
end
handle_rmn_sd_sy(tokens, options) click to toggle source

Handle repeater-month-name/scalar-day/scalar-year

# File lib/chronic/handlers.rb, line 142
def handle_rmn_sd_sy(tokens, options)
  month = tokens[0].get_tag(RepeaterMonthName).index
  day = tokens[1].get_tag(ScalarDay).type
  year = tokens[2].get_tag(ScalarYear).type
  time_tokens = tokens.last(tokens.size - 3)

  return if month_overflow?(year, month, day)

  begin
    day_start = Chronic.time_class.local(year, month, day)
    day_or_time(day_start, time_tokens, options)
  rescue ArgumentError
    nil
  end
end
handle_rmn_sy(tokens, options) click to toggle source

Handle repeater-month-name/scalar-year

# File lib/chronic/handlers.rb, line 113
def handle_rmn_sy(tokens, options)
  month = tokens[0].get_tag(RepeaterMonthName).index
  year = tokens[1].get_tag(ScalarYear).type

  if month == 12
    next_month_year = year + 1
    next_month_month = 1
  else
    next_month_year = year
    next_month_month = month + 1
  end

  begin
    end_time = Chronic.time_class.local(next_month_year, next_month_month)
    Span.new(Chronic.time_class.local(year, month), end_time)
  rescue ArgumentError
    nil
  end
end
handle_s_r_a_s_r_p_a(tokens, options) click to toggle source
# File lib/chronic/handlers.rb, line 453
def handle_s_r_a_s_r_p_a(tokens, options)
  anchor_span = get_anchor(tokens[4..tokens.size - 1], options)

  span = handle_srp(tokens[0..1]+tokens[4..6], anchor_span, options)
  handle_srp(tokens[2..3]+tokens[4..6], span, options)
end
handle_s_r_p(tokens, options) click to toggle source

Handle scalar/repeater/pointer

# File lib/chronic/handlers.rb, line 435
def handle_s_r_p(tokens, options)
  span = Span.new(self.now, self.now + 1)

  handle_srp(tokens, span, options)
end
handle_s_r_p_a(tokens, options) click to toggle source

Handle scalar/repeater/pointer/anchor

# File lib/chronic/handlers.rb, line 448
def handle_s_r_p_a(tokens, options)
  anchor_span = get_anchor(tokens[3..tokens.size - 1], options)
  handle_srp(tokens, anchor_span, options)
end
handle_sd_rmn(tokens, options) click to toggle source

Handle scalar-day/repeater-month-name

# File lib/chronic/handlers.rb, line 86
def handle_sd_rmn(tokens, options)
  month = tokens[1].get_tag(RepeaterMonthName)
  day = tokens[0].get_tag(ScalarDay).type

  return if month_overflow?(self.now.year, month.index, day)

  handle_m_d(month, day, tokens[2..tokens.size], options)
end
handle_sd_rmn_sy(tokens, options) click to toggle source

Handle scalar-day/repeater-month-name/scalar-year

# File lib/chronic/handlers.rb, line 193
def handle_sd_rmn_sy(tokens, options)
  new_tokens = [tokens[1], tokens[0], tokens[2]]
  time_tokens = tokens.last(tokens.size - 3)
  handle_rmn_sd_sy(new_tokens + time_tokens, options)
end
handle_sd_sm(tokens, options) click to toggle source

Handle scalar-day/scalar-month

# File lib/chronic/handlers.rb, line 249
def handle_sd_sm(tokens, options)
  new_tokens = [tokens[1], tokens[0]]
  time_tokens = tokens.last(tokens.size - 2)
  handle_sm_sd(new_tokens + time_tokens, options)
end
handle_sd_sm_sy(tokens, options) click to toggle source

Handle scalar-day/scalar-month/scalar-year (endian little)

# File lib/chronic/handlers.rb, line 217
def handle_sd_sm_sy(tokens, options)
  new_tokens = [tokens[1], tokens[0], tokens[2]]
  time_tokens = tokens.last(tokens.size - 3)
  handle_sm_sd_sy(new_tokens + time_tokens, options)
end
handle_sm_rmn_sy(tokens, options) click to toggle source
# File lib/chronic/handlers.rb, line 392
def handle_sm_rmn_sy(tokens, options)
  day = tokens[0].get_tag(ScalarDay).type
  month = tokens[1].get_tag(RepeaterMonthName).index
  year = tokens[2].get_tag(ScalarYear).type
  if tokens.size > 3
    time = get_anchor([tokens.last], options).begin
    h, m, s = time.hour, time.min, time.sec
    time = Chronic.time_class.local(year, month, day, h, m, s)
    end_time = Chronic.time_class.local(year, month, day + 1, h, m, s)
  else
    time = Chronic.time_class.local(year, month, day)
    day += 1 unless day >= 31
    end_time = Chronic.time_class.local(year, month, day)
  end
  Span.new(time, end_time)
end
handle_sm_sd(tokens, options) click to toggle source

Handle scalar-month/scalar-day

# File lib/chronic/handlers.rb, line 231
def handle_sm_sd(tokens, options)
  month = tokens[0].get_tag(ScalarMonth).type
  day = tokens[1].get_tag(ScalarDay).type
  year = self.now.year
  time_tokens = tokens.last(tokens.size - 2)

  return if month_overflow?(year, month, day)

  begin
    day_start = Chronic.time_class.local(year, month, day)
    day_start = Chronic.time_class.local(year + 1, month, day) if options[:context] == :future && day_start < now
    day_or_time(day_start, time_tokens, options)
  rescue ArgumentError
    nil
  end
end
handle_sm_sd_sy(tokens, options) click to toggle source

Handle scalar-month/scalar-day/scalar-year (endian middle)

# File lib/chronic/handlers.rb, line 200
def handle_sm_sd_sy(tokens, options)
  month = tokens[0].get_tag(ScalarMonth).type
  day = tokens[1].get_tag(ScalarDay).type
  year = tokens[2].get_tag(ScalarYear).type
  time_tokens = tokens.last(tokens.size - 3)

  return if month_overflow?(year, month, day)

  begin
    day_start = Chronic.time_class.local(year, month, day)
    day_or_time(day_start, time_tokens, options)
  rescue ArgumentError
    nil
  end
end
handle_sm_sy(tokens, options) click to toggle source

Handle scalar-month/scalar-year

# File lib/chronic/handlers.rb, line 273
def handle_sm_sy(tokens, options)
  month = tokens[0].get_tag(ScalarMonth).type
  year = tokens[1].get_tag(ScalarYear).type
  handle_year_and_month(year, month)
end
handle_srp(tokens, span, options) click to toggle source

Handle scalar/repeater/pointer helper

# File lib/chronic/handlers.rb, line 426
def handle_srp(tokens, span, options)
  distance = tokens[0].get_tag(Scalar).type
  repeater = tokens[1].get_tag(Repeater)
  pointer = tokens[2].get_tag(Pointer).type

  repeater.offset(span, distance, pointer) if repeater.respond_to?(:offset)
end
handle_sy_rmn_od(tokens, options) click to toggle source
# File lib/chronic/handlers.rb, line 69
def handle_sy_rmn_od(tokens, options)
  year = tokens[0].get_tag(ScalarYear).type
  month = tokens[1].get_tag(RepeaterMonthName).index
  day = tokens[2].get_tag(OrdinalDay).type
  time_tokens = tokens.last(tokens.size - 3)

  return if month_overflow?(year, month, day)

  begin
    day_start = Chronic.time_class.local(year, month, day)
    day_or_time(day_start, time_tokens, options)
  rescue ArgumentError
    nil
  end
end
handle_sy_sm(tokens, options) click to toggle source

Handle scalar-year/scalar-month

# File lib/chronic/handlers.rb, line 280
def handle_sy_sm(tokens, options)
  year = tokens[0].get_tag(ScalarYear).type
  month = tokens[1].get_tag(ScalarMonth).type
  handle_year_and_month(year, month)
end
handle_sy_sm_sd(tokens, options) click to toggle source

Handle scalar-year/scalar-month/scalar-day

# File lib/chronic/handlers.rb, line 224
def handle_sy_sm_sd(tokens, options)
  new_tokens = [tokens[1], tokens[2], tokens[0]]
  time_tokens = tokens.last(tokens.size - 3)
  handle_sm_sd_sy(new_tokens + time_tokens, options)
end
handle_year_and_month(year, month) click to toggle source
# File lib/chronic/handlers.rb, line 255
def handle_year_and_month(year, month)
  if month == 12
    next_month_year = year + 1
    next_month_month = 1
  else
    next_month_year = year
    next_month_month = month + 1
  end

  begin
    end_time = Chronic.time_class.local(next_month_year, next_month_month)
    Span.new(Chronic.time_class.local(year, month), end_time)
  rescue ArgumentError
    nil
  end
end
month_overflow?(year, month, day) click to toggle source
# File lib/chronic/handlers.rb, line 546
def month_overflow?(year, month, day)
  if ::Date.leap?(year)
    day > RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
  else
    day > RepeaterMonth::MONTH_DAYS[month - 1]
  end
rescue ArgumentError
  false
end
time_with_rollover(year, month, day) click to toggle source
# File lib/chronic/handlers.rb, line 572
def time_with_rollover(year, month, day)
  date_parts =
    if month_overflow?(year, month, day)
      if month == 12
        [year + 1, 1, 1]
      else
        [year, month + 1, 1]
      end
    else
      [year, month, day]
    end
  Chronic.time_class.local(*date_parts)
end