natural_lang_date_parser 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/natural_lang_date_parser.rb +118 -129
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 593e0b14f73dc639297a9f8b5570fde166dffefc
|
4
|
+
data.tar.gz: d8bc85676fb0c94ed1559ecc7519499956631286
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc55a9880091c212d79624a0638ab92a4b2c142c1ea82d76c2d4cde628d95509c061e19c21fda07ab5a25a4fe8ba5ae7c7236a091dac722cda869ac14525a392
|
7
|
+
data.tar.gz: 08d89a3e6372075d9a4dc937da3c12774ad4ba88ba4d74d10dbf771ecae7044c4464d4e893c80210f54e9ee00e3884bb7803329579bc577188b407c3bc5887f7
|
@@ -5,156 +5,145 @@ module NaturalLangDateParser
|
|
5
5
|
require 'active_support/all'
|
6
6
|
|
7
7
|
EXISTING_PATTERNS = {
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
durations: 'minute|minutes|hour|hours|day|days|week|weeks|month|months|year|years',
|
9
|
+
relative_tense: 'last|previous|next',
|
10
|
+
explicit_dates: 'today|tomorrow|yesterday|day after tomorrow|day before yesterday',
|
11
|
+
months: 'january|february|march|may|june|july|august|september|october|november|december',
|
12
|
+
weekdays: 'sunday|monday|tuesday|wednesday|thursday|friday|saturday',
|
12
13
|
}
|
14
|
+
|
13
15
|
class Parser
|
14
16
|
|
15
17
|
def initialize(datetime)
|
16
18
|
@current_datetime = DateTime.now
|
17
19
|
@input_params = datetime
|
18
|
-
@formatted_params = nil
|
19
20
|
end
|
20
|
-
|
21
|
+
|
22
|
+
# Formatting the input before parsing.
|
21
23
|
def format_input
|
22
24
|
# removing white spaces at the beginning and the end
|
23
|
-
@input_params.strip()
|
24
|
-
|
25
|
-
# p @formatted_params
|
26
|
-
end
|
27
|
-
|
28
|
-
def parse_input
|
29
|
-
# sanitise the input before processing.
|
30
|
-
format_input
|
31
|
-
|
32
|
-
# check if its today
|
33
|
-
if @input_params == 'today'
|
34
|
-
DateTime.now
|
35
|
-
# check if its past
|
36
|
-
elsif !(@formatted_params & EXISTING_PATTERNS[:past]).empty?
|
37
|
-
calculate_past_date
|
38
|
-
# check if its future
|
39
|
-
elsif !(@formatted_params & EXISTING_PATTERNS[:future]).empty?
|
40
|
-
calculate_future_date
|
41
|
-
# Fallback to Ruby Date parser
|
42
|
-
else
|
43
|
-
# replacing noon with 12pm. Further scenarios can be added and moved to a new method in future
|
44
|
-
@input_params.gsub!(/noon/,'12pm')
|
45
|
-
DateTime.parse(@input_params)
|
46
|
-
end
|
47
|
-
rescue
|
48
|
-
puts "Sorry!! Something went wrong while interpreting your input. Pls. check and try again."
|
49
|
-
end
|
25
|
+
@input_params = @input_params.downcase.strip()
|
26
|
+
end
|
50
27
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# Removing preposition like on, at
|
70
|
-
temp_formatted_params = @formatted_params.reject{|item| item == /on|at/i }
|
71
|
-
|
72
|
-
# check if the user is inputing a weekeday
|
73
|
-
if is_weekday? temp_formatted_params[1]
|
74
|
-
Date.today.beginning_of_week(:temp_formatted_params[1])
|
75
|
-
# if its one among year, day, month
|
76
|
-
else
|
77
|
-
calculate_time(temp_formatted_params[1],1,"ago")
|
28
|
+
# Parsing the Input provided by the user.
|
29
|
+
def parse_input
|
30
|
+
format_input
|
31
|
+
|
32
|
+
p "Input is #{@input_params}"
|
33
|
+
#begin
|
34
|
+
# check if the input refers to an explicit datetime like today etc
|
35
|
+
if explicit_date? @input_params
|
36
|
+
interpret_explicit_date @input_params
|
37
|
+
# check if the input refers to relative input like next friday or next month etc
|
38
|
+
elsif relative_date? @input_params
|
39
|
+
interpret_relative_date @input_params
|
40
|
+
# check if the input refers to a past of future date and interpret it
|
41
|
+
elsif date = past_or_future_date(@input_params)
|
42
|
+
date
|
43
|
+
# Try Ruby Date Parser
|
44
|
+
else
|
45
|
+
DateTime.parse(@input_params)
|
78
46
|
end
|
47
|
+
#rescue
|
48
|
+
# p "Sorry!! Something went wrong. Pls. check and try again"
|
49
|
+
#end
|
50
|
+
end
|
79
51
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
52
|
+
# check if the input refers to an explicit datetime like today etc
|
53
|
+
def explicit_date?(date)
|
54
|
+
!(/(?<relative_date>#{EXISTING_PATTERNS[:explicit_dates]})/.match(date)).nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def past_or_future_date date
|
58
|
+
# when date is of the form 2 weeks ago
|
59
|
+
if parsed_data = (/(?<quantity>\d+) (?<duration>#{EXISTING_PATTERNS[:durations]}) (?<tense>ago|later|after)*/.match(date))
|
60
|
+
calculate_datetime(parsed_data[:duration], parsed_data[:quantity].to_i,find_tense(parsed_data[:tense]))
|
61
|
+
# when date is of the form in 3 hours
|
62
|
+
elsif parsed_data = (/(?<tense>after|in|before) (?<quantity>\d+) (?<duration>#{EXISTING_PATTERNS[:durations]})/.match(date))
|
63
|
+
calculate_datetime(parsed_data[:duration], parsed_data[:quantity].to_i,find_tense(parsed_data[:tense]))
|
64
|
+
else
|
65
|
+
false
|
90
66
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
# can be exteneded with options like next full moon, next christmas etc
|
115
|
-
if is_weekday? @formatted_params[1]
|
116
|
-
date_of_next @formatted_params[1]
|
117
|
-
# it its one among year, day, month
|
118
|
-
else
|
119
|
-
calculate_time(@formatted_params[1],1,"from_now")
|
120
|
-
end
|
121
|
-
|
122
|
-
# case for string non-immediate future like 'later','in
|
123
|
-
elsif !(future_params[:future_value] & ['later','in']).empty?
|
124
|
-
future_params[:future_type] = 'future'
|
125
|
-
temp_formatted_params = @formatted_params.reject{|item| item =~ /later|in/i }
|
126
|
-
|
127
|
-
# Extracting Values
|
128
|
-
future_params[:date_quantity] = temp_formatted_params[0]
|
129
|
-
future_params[:date_type] = temp_formatted_params[1]
|
130
|
-
calculate_time(future_params[:date_type],future_params[:date_quantity].to_i,"from_now")
|
67
|
+
end
|
68
|
+
|
69
|
+
# check whether date is of the form next friday or next month etc & return boolean
|
70
|
+
def relative_date? date
|
71
|
+
all_durations = EXISTING_PATTERNS[:weekdays] + EXISTING_PATTERNS[:months] + EXISTING_PATTERNS[:durations]
|
72
|
+
!(/(#{EXISTING_PATTERNS[:relative_tense]}) (#{all_durations})/.match(date)).nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
# asiigning values for few explicit dates like tomorrow, yesterday etc.
|
76
|
+
def interpret_explicit_date date
|
77
|
+
case date
|
78
|
+
when 'today'
|
79
|
+
DateTime.now
|
80
|
+
when 'tomorrow'
|
81
|
+
1.days.from_now
|
82
|
+
when 'yesterday'
|
83
|
+
1.days.ago
|
84
|
+
when 'day after tomorrow'
|
85
|
+
2.days.from_now
|
86
|
+
when 'day before yesterday'
|
87
|
+
2.days.ago
|
88
|
+
else
|
89
|
+
nil
|
131
90
|
end
|
132
|
-
|
91
|
+
end
|
92
|
+
|
93
|
+
# Parsing relative date like next friday or next month
|
94
|
+
def interpret_relative_date date
|
95
|
+
all_durations = EXISTING_PATTERNS[:weekdays] + EXISTING_PATTERNS[:months] + EXISTING_PATTERNS[:durations]
|
96
|
+
relative_date = /(?<tense>#{EXISTING_PATTERNS[:relative_tense]}) (?<type>#{all_durations})(\s at)*/.match(date)
|
97
|
+
|
98
|
+
# Check if the user is referring to a weekday
|
99
|
+
if weekday?(relative_date[:type])
|
100
|
+
if (relative_date[:tense] == 'next')
|
101
|
+
date_of_next(relative_date[:type])
|
102
|
+
else
|
103
|
+
date_of_previous(relative_date[:type])
|
104
|
+
end
|
105
|
+
else
|
106
|
+
tense = (relative_date[:tense] == 'next') ? 'from_now' : 'ago'
|
107
|
+
calculate_datetime(relative_date[:type], 1, tense)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def find_tense data
|
113
|
+
if ['ago', 'before'].include? data
|
114
|
+
'ago'
|
115
|
+
else
|
116
|
+
'from_now'
|
117
|
+
end
|
118
|
+
end
|
133
119
|
|
134
|
-
|
135
|
-
|
136
|
-
day && (EXISTING_PATTERNS[:
|
137
|
-
|
120
|
+
# returns if its a valid day of the week
|
121
|
+
def weekday?(day)
|
122
|
+
day && (EXISTING_PATTERNS[:weekdays].split('|').include? day)
|
123
|
+
end
|
138
124
|
|
139
125
|
# Return the next specific weekeday. Example: next tuesday
|
140
|
-
|
141
|
-
|
142
|
-
delta =
|
143
|
-
|
126
|
+
def date_of_next(day)
|
127
|
+
day_required = DateTime.parse(day)
|
128
|
+
delta = day_required > DateTime.now ? 0 : 7
|
129
|
+
(day_required + delta)
|
144
130
|
end
|
145
131
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
132
|
+
# Return the previous specific weekeday. Example: previous tuesday
|
133
|
+
def date_of_previous(day)
|
134
|
+
day_required = DateTime.parse(day)
|
135
|
+
delta = day_required < DateTime.now ? 0 : 7
|
136
|
+
(day_required - delta)
|
137
|
+
end
|
153
138
|
|
154
|
-
|
155
|
-
|
139
|
+
# Defining the DateTime object based on parameters.
|
140
|
+
def calculate_datetime(type, quantity, tense)
|
141
|
+
# converting week to days as ruby doesnt have explicit method for week.
|
142
|
+
if type.singularize == 'week'
|
143
|
+
type = 'days'
|
144
|
+
quantity = quantity * 7
|
145
|
+
end
|
156
146
|
quantity.send(type).send(tense)
|
157
147
|
end
|
158
|
-
|
159
148
|
end
|
160
149
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: natural_lang_date_parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sriram R
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|