Skip to content

Instantly share code, notes, and snippets.

@sriram15690
Created October 6, 2016 07:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sriram15690/69ea53f96393fc922074414dc243465d to your computer and use it in GitHub Desktop.
Save sriram15690/69ea53f96393fc922074414dc243465d to your computer and use it in GitHub Desktop.
#module DateParser
#DateTime.new(2016,06,06,17,20,10).strftime("%d/%m/%Y %I,'M%p")
require "date"
require 'active_support/all'
EXISTING_PATTERNS = {
months: ['january','february','march','may','june','july','august','september', 'october','november','december'],
days: ['sunday','monday','tuesday', 'wednesday','thursday','friday','saturday'],
past: ["before","ago", 'yesterday', 'last','previous'],
future: ['after','later','tomorrow','next', 'in']
}
class NaturalDateParser
def initialize(datetime)
@current_datetime = DateTime.now
@input_params = datetime
@formatted_params = nil
end
def format_input
# removing white spaces at the beginning and the end
@input_params.strip()
@formatted_params = @input_params.split(" ").map {|x| x.downcase.singularize}
p @formatted_params
end
def parse_input
# sanitise the input before processing.
format_input
# check if its today
if @input_params == 'today'
DateTime.now
# check if its past
elsif !(@formatted_params & EXISTING_PATTERNS[:past]).empty?
calculate_past_date
# check if its future
elsif !(@formatted_params & EXISTING_PATTERNS[:future]).empty?
calculate_future_date
# Fallback to Ruby Date parser
else
# replacing noon with 12pm. Further scenarios can be added and moved to a new method in future
@input_params.gsub!(/noon/,'12pm')
DateTime.parse(@input_params)
end
rescue
puts "Sorry!! Something went wrong while interpreting your input. Pls. check and try again."
end
def update_time(type, quantity, tense)
type = type.pluralize
quantity.send(type).send(tense)
end
def calculate_past_date
# storing the various parameters
past_params = {
past_type: nil,
past_value: nil,
date_type: nil,
date_quantity: nil,
past_index: nil
}
past_params[:past_value] = @formatted_params & EXISTING_PATTERNS[:past]
# case for yesterday
if past_params[:past_value] && @formatted_params.length == 1
update_time('days',1,"ago")
# case for string containing last or previous ( Immediate Past)
elsif is_immediate_past?(past_params[:past_value])
# Removing preposition like on, at
temp_formatted_params = @formatted_params.reject{|item| item == /on|at/i }
# check if the user is inputing a weekeday
if is_weekday? temp_formatted_params[1]
Date.today.beginning_of_week(:temp_formatted_params[1])
# if its one among year, day, month
else
update_time(temp_formatted_params[1],1,"ago")
end
# case for string containing ago or before etc (non-immediate past)
elsif !(past_params[:past_value] & ['ago','before']).empty?
temp_formatted_params = @formatted_params.reject{|item| item == /on|at|ago|before/i }
# Extracting Values
past_params[:date_quantity] = temp_formatted_params[0]
past_params[:date_type] = temp_formatted_params[1]
# Calcuate Datetime
update_time(past_params[:date_type],past_params[:date_quantity].to_i,"ago")
end
end
def calculate_future_date
# storing the various parameters
future_params = {
future_type: nil,
future_value: nil,
date_type: nil,
date_quantity: nil,
future_index: nil
}
future_params[:future_value] = @formatted_params & EXISTING_PATTERNS[:future]
# case for tomorrow
if future_params[:future_value] == ["tomorrow"] && @formatted_params.length == 1
update_time('days',1,"from_now")
# Case for immediate future
elsif is_immediate_future? @formatted_params
future_params[:future_type] ='immediate'
future_params[:date_type] = @formatted_params[1]
# check if the user is inputing a weekeday
# can be exteneded with options like next full moon, next christmas etc
if is_weekday? @formatted_params[1]
date_of_next @formatted_params[1]
# it its one among year, day, month
else
update_time(@formatted_params[1],1,"from_now")
end
# case for string non-immediate future
elsif !(future_params[:future_value] & ['later','in']).empty?
future_params[:future_type] = 'future'
temp_formatted_params = @formatted_params.reject{|item| item =~ /later|in/i }
# Extracting Values
future_params[:date_quantity] = temp_formatted_params[0]
future_params[:date_type] = temp_formatted_params[1]
update_time(future_params[:date_type],future_params[:date_quantity].to_i,"from_now")
end
end
# reutrns if its a valid day of the week
def is_weekday?(day)
day && (EXISTING_PATTERNS[:days].include? day)
end
# Return the next specific weekeday. Example: next tuesday
def date_of_next(day)
date = Date.parse(day)
delta = date > Date.today ? 0 : 7
date + delta
end
def is_immediate_future?(data)
data.include? "next"
end
def is_immediate_past?(data)
!(data & ['last', 'previous']).empty?
end
end
#end
#DateParser.format_input()
x = NaturalDateParser.new("tomorrow")
p x.parse_input
x = NaturalDateParser.new("next friday")
p x.parse_input
x = NaturalDateParser.new("next tuesday")
p x.parse_input
x = NaturalDateParser.new("next month")
p x.parse_input
x = NaturalDateParser.new("today")
p x.parse_input
x = NaturalDateParser.new("yesterday")
p x.parse_input
x = NaturalDateParser.new("2 weeks later")
p x.parse_input
x = NaturalDateParser.new("in 2 weeks")
p x.parse_input
x = NaturalDateParser.new("in 3 hours")
p x.parse_input
x = NaturalDateParser.new("2 Weeks Ago")
p x.parse_input
x = NaturalDateParser.new("on december 5th at 4pm")
p x.parse_input
x = NaturalDateParser.new("on december 5th at noon")
p x.parse_input
x = NaturalDateParser.new("Last Month")
p x.parse_input
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment