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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/natural_lang_date_parser.rb +118 -129
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7117f3f7ed8419168e587422b3fedf705b0997d9
4
- data.tar.gz: f5655954c0ed382fdb8d338426e032d00a2a602f
3
+ metadata.gz: 593e0b14f73dc639297a9f8b5570fde166dffefc
4
+ data.tar.gz: d8bc85676fb0c94ed1559ecc7519499956631286
5
5
  SHA512:
6
- metadata.gz: 97f6a4a5ca77f2df728bafd3909edc483a4139cfc8cb0174ab60779eda02dcefff4865fa350389d96b4c5b37928bbbbea31880b0cdbafa3d2fb2708e587c063c
7
- data.tar.gz: 4c0329cdfb1fd93f6564a0a2d65913b2b79ca698cdbb745c64776c097bf2209f9bb8e883df2bc8e10817f69b22f5b94524d490ed3c39495c4d2be0376fc6a913
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
- months: ['january','february','march','may','june','july','august','september', 'october','november','december'],
9
- days: ['sunday','monday','tuesday', 'wednesday','thursday','friday','saturday'],
10
- past: ["before","ago", 'yesterday', 'last','previous'],
11
- future: ['after','later','tomorrow','next', 'in']
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
- @formatted_params = @input_params.split(" ").map {|x| x.downcase.singularize}
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
- def calculate_past_date
52
- # storing the various parameters
53
- past_params = {
54
- past_type: nil,
55
- past_value: nil,
56
- date_type: nil,
57
- date_quantity: nil,
58
- past_index: nil
59
- }
60
-
61
- past_params[:past_value] = @formatted_params & EXISTING_PATTERNS[:past]
62
-
63
- # case for yesterday
64
- if past_params[:past_value] && @formatted_params.length == 1
65
- calculate_time('days',1,"ago")
66
-
67
- # case for string containing last or previous ( Immediate Past)
68
- elsif is_immediate_past?(past_params[:past_value])
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
- # case for string containing ago or before etc (non-immediate past)
81
- elsif !(past_params[:past_value] & ['ago','before']).empty?
82
- temp_formatted_params = @formatted_params.reject{|item| item == /on|at|ago|before/i }
83
-
84
- # Extracting Values
85
- past_params[:date_quantity] = temp_formatted_params[0]
86
- past_params[:date_type] = temp_formatted_params[1]
87
-
88
- # Calcuate Datetime
89
- calculate_time(past_params[:date_type],past_params[:date_quantity].to_i,"ago")
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
- end
92
-
93
- def calculate_future_date
94
- # storing the various parameters
95
- future_params = {
96
- future_type: nil,
97
- future_value: nil,
98
- date_type: nil,
99
- date_quantity: nil,
100
- future_index: nil
101
- }
102
- future_params[:future_value] = @formatted_params & EXISTING_PATTERNS[:future]
103
-
104
- # case for tomorrow
105
- if future_params[:future_value] == ["tomorrow"] && @formatted_params.length == 1
106
- calculate_time('days',1,"from_now")
107
-
108
- # Case for immediate future
109
- elsif is_immediate_future? @formatted_params
110
- future_params[:future_type] ='immediate'
111
- future_params[:date_type] = @formatted_params[1]
112
-
113
- # check if the user is inputing a weekeday
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
- end
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
- # reutrns if its a valid day of the week
135
- def is_weekday?(day)
136
- day && (EXISTING_PATTERNS[:days].include? day)
137
- end
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
- def date_of_next(day)
141
- date = Date.parse(day)
142
- delta = date > Date.today ? 0 : 7
143
- date + delta
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
- def is_immediate_future?(data)
147
- data.include? "next"
148
- end
149
-
150
- def is_immediate_past?(data)
151
- !(data & ['last', 'previous']).empty?
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
- def calculate_time(type, quantity, tense)
155
- type = type.pluralize
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.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-06 00:00:00.000000000 Z
11
+ date: 2016-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler