chronic 0.4.1 → 0.4.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.
- data/HISTORY.md +9 -2
- data/Rakefile +0 -8
- data/chronic.gemspec +1 -1
- data/lib/chronic.rb +60 -47
- data/lib/chronic/chronic.rb +221 -90
- data/lib/chronic/grabber.rb +10 -1
- data/lib/chronic/handlers.rb +44 -186
- data/lib/chronic/mini_date.rb +6 -2
- data/lib/chronic/ordinal.rb +12 -1
- data/lib/chronic/pointer.rb +10 -1
- data/lib/chronic/repeater.rb +20 -1
- data/lib/chronic/repeaters/repeater_month.rb +13 -1
- data/lib/chronic/scalar.rb +29 -1
- data/lib/chronic/separator.rb +18 -1
- data/lib/chronic/time_zone.rb +10 -1
- data/lib/chronic/token.rb +14 -4
- data/test/test_Chronic.rb +52 -2
- data/test/test_RepeaterMonth.rb +4 -0
- metadata +2 -2
data/lib/chronic/grabber.rb
CHANGED
@@ -1,11 +1,20 @@
|
|
1
1
|
module Chronic
|
2
|
-
class Grabber < Tag
|
2
|
+
class Grabber < Tag
|
3
|
+
|
4
|
+
# Scan an Array of {Token}s and apply any necessary Grabber tags to
|
5
|
+
# each token
|
6
|
+
#
|
7
|
+
# @param [Array<Token>] tokens Array of tokens to scan
|
8
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
9
|
+
# @return [Array] list of tokens
|
3
10
|
def self.scan(tokens, options)
|
4
11
|
tokens.each_index do |i|
|
5
12
|
if t = scan_for_all(tokens[i]) then tokens[i].tag(t); next end
|
6
13
|
end
|
7
14
|
end
|
8
15
|
|
16
|
+
# @param [Token] token
|
17
|
+
# @return [Grabber, nil]
|
9
18
|
def self.scan_for_all(token)
|
10
19
|
scan_for token, self,
|
11
20
|
{
|
data/lib/chronic/handlers.rb
CHANGED
@@ -1,156 +1,10 @@
|
|
1
1
|
module Chronic
|
2
|
+
module Handlers
|
3
|
+
module_function
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
def definitions(options={}) #:nodoc:
|
6
|
-
options[:endian_precedence] ||= [:middle, :little]
|
7
|
-
# ensure the endian precedence is exactly two elements long
|
8
|
-
raise ChronicPain, "More than two elements specified for endian precedence array" unless options[:endian_precedence].length == 2
|
9
|
-
|
10
|
-
# handler for dd/mm/yyyy
|
11
|
-
@little_endian_handler ||= Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
|
12
|
-
|
13
|
-
# handler for mm/dd/yyyy
|
14
|
-
@middle_endian_handler ||= Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy)
|
15
|
-
|
16
|
-
# ensure we have valid endian values
|
17
|
-
options[:endian_precedence].each do |e|
|
18
|
-
raise ChronicPain, "Unknown endian type: #{e.to_s}" unless instance_variable_defined?(endian_variable_name_for(e))
|
19
|
-
end
|
20
|
-
|
21
|
-
@definitions ||= {
|
22
|
-
:time => [
|
23
|
-
Handler.new([:repeater_time, :repeater_day_portion?], nil)
|
24
|
-
],
|
25
|
-
|
26
|
-
:date => [
|
27
|
-
Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :separator_slash_or_dash?, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),
|
28
|
-
Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
|
29
|
-
Handler.new([:repeater_month_name, :ordinal_day, :scalar_year], :handle_rmn_od_sy),
|
30
|
-
Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
|
31
|
-
Handler.new([:repeater_month_name, :ordinal_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_od_sy),
|
32
|
-
Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
|
33
|
-
Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :scalar_day], :handle_rmn_sd_on),
|
34
|
-
Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
|
35
|
-
Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :ordinal_day], :handle_rmn_od_on),
|
36
|
-
Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),
|
37
|
-
Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),
|
38
|
-
@middle_endian_handler,
|
39
|
-
@little_endian_handler,
|
40
|
-
Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
|
41
|
-
Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)
|
42
|
-
],
|
43
|
-
|
44
|
-
# tonight at 7pm
|
45
|
-
:anchor => [
|
46
|
-
Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
|
47
|
-
Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
|
48
|
-
Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)
|
49
|
-
],
|
50
|
-
|
51
|
-
# 3 weeks from now, in 2 months
|
52
|
-
:arrow => [
|
53
|
-
Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
|
54
|
-
Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
|
55
|
-
Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)
|
56
|
-
],
|
57
|
-
|
58
|
-
# 3rd week in march
|
59
|
-
:narrow => [
|
60
|
-
Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
|
61
|
-
Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)
|
62
|
-
]
|
63
|
-
}
|
64
|
-
|
65
|
-
apply_endian_precedences(options[:endian_precedence])
|
66
|
-
|
67
|
-
@definitions
|
68
|
-
end
|
69
|
-
|
70
|
-
def tokens_to_span(tokens, options) #:nodoc:
|
71
|
-
# maybe it's a specific date
|
72
|
-
|
73
|
-
definitions = self.definitions(options)
|
74
|
-
definitions[:date].each do |handler|
|
75
|
-
if handler.match(tokens, definitions)
|
76
|
-
puts "-date" if Chronic.debug
|
77
|
-
good_tokens = tokens.select { |o| !o.get_tag Separator }
|
78
|
-
return self.send(handler.handler_method, good_tokens, options)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# I guess it's not a specific date, maybe it's just an anchor
|
83
|
-
|
84
|
-
definitions[:anchor].each do |handler|
|
85
|
-
if handler.match(tokens, definitions)
|
86
|
-
puts "-anchor" if Chronic.debug
|
87
|
-
good_tokens = tokens.select { |o| !o.get_tag Separator }
|
88
|
-
return self.send(handler.handler_method, good_tokens, options)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# not an anchor, perhaps it's an arrow
|
93
|
-
|
94
|
-
definitions[:arrow].each do |handler|
|
95
|
-
if handler.match(tokens, definitions)
|
96
|
-
puts "-arrow" if Chronic.debug
|
97
|
-
good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlashOrDash) || o.get_tag(SeparatorComma) }
|
98
|
-
return self.send(handler.handler_method, good_tokens, options)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# not an arrow, let's hope it's a narrow
|
103
|
-
|
104
|
-
definitions[:narrow].each do |handler|
|
105
|
-
if handler.match(tokens, definitions)
|
106
|
-
puts "-narrow" if Chronic.debug
|
107
|
-
#good_tokens = tokens.select { |o| !o.get_tag Separator }
|
108
|
-
return self.send(handler.handler_method, tokens, options)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
# I guess you're out of luck!
|
113
|
-
puts "-none" if Chronic.debug
|
114
|
-
return nil
|
115
|
-
end
|
116
|
-
|
117
|
-
#--------------
|
118
|
-
|
119
|
-
def apply_endian_precedences(precedences)
|
120
|
-
date_defs = @definitions[:date]
|
121
|
-
|
122
|
-
# map the precedence array to indices on @definitions[:date]
|
123
|
-
indices = precedences.map { |e|
|
124
|
-
handler = instance_variable_get(endian_variable_name_for(e))
|
125
|
-
date_defs.index(handler)
|
126
|
-
}
|
127
|
-
|
128
|
-
# swap the handlers if we discover they are at odds with the desired preferences
|
129
|
-
swap(date_defs, indices.first, indices.last) if indices.first > indices.last
|
130
|
-
end
|
131
|
-
|
132
|
-
def endian_variable_name_for(e)
|
133
|
-
"@#{e.to_s}_endian_handler".to_sym
|
134
|
-
end
|
135
|
-
|
136
|
-
# exchange two elements in an array
|
137
|
-
def swap(arr, a, b); arr[a], arr[b] = arr[b], arr[a]; end
|
138
|
-
|
139
|
-
def day_or_time(day_start, time_tokens, options)
|
140
|
-
outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
|
141
|
-
|
142
|
-
if !time_tokens.empty?
|
143
|
-
@now = outer_span.begin
|
144
|
-
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
|
145
|
-
else
|
146
|
-
outer_span
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
#--------------
|
151
|
-
|
5
|
+
# Handle month/day
|
152
6
|
def handle_m_d(month, day, time_tokens, options) #:nodoc:
|
153
|
-
month.start =
|
7
|
+
month.start = Chronic.now
|
154
8
|
span = month.this(options[:context])
|
155
9
|
|
156
10
|
day_start = Chronic.time_class.local(span.begin.year, span.begin.month, day)
|
@@ -158,10 +12,12 @@ module Chronic
|
|
158
12
|
day_or_time(day_start, time_tokens, options)
|
159
13
|
end
|
160
14
|
|
15
|
+
# Handle repeater-month-name/scalar-day
|
161
16
|
def handle_rmn_sd(tokens, options) #:nodoc:
|
162
17
|
handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(ScalarDay).type, tokens[2..tokens.size], options)
|
163
18
|
end
|
164
19
|
|
20
|
+
# Handle repeater-month-name/scalar-day with separator-on
|
165
21
|
def handle_rmn_sd_on(tokens, options) #:nodoc:
|
166
22
|
if tokens.size > 3
|
167
23
|
handle_m_d(tokens[2].get_tag(RepeaterMonthName), tokens[3].get_tag(ScalarDay).type, tokens[0..1], options)
|
@@ -170,10 +26,12 @@ module Chronic
|
|
170
26
|
end
|
171
27
|
end
|
172
28
|
|
29
|
+
# Handle repeater-month-name/ordinal-day
|
173
30
|
def handle_rmn_od(tokens, options) #:nodoc:
|
174
31
|
handle_m_d(tokens[0].get_tag(RepeaterMonthName), tokens[1].get_tag(OrdinalDay).type, tokens[2..tokens.size], options)
|
175
32
|
end
|
176
33
|
|
34
|
+
# Handle repeater-month-name/ordinal-day with separator-on
|
177
35
|
def handle_rmn_od_on(tokens, options) #:nodoc:
|
178
36
|
if tokens.size > 3
|
179
37
|
handle_m_d(tokens[2].get_tag(RepeaterMonthName), tokens[3].get_tag(OrdinalDay).type, tokens[0..1], options)
|
@@ -182,6 +40,7 @@ module Chronic
|
|
182
40
|
end
|
183
41
|
end
|
184
42
|
|
43
|
+
# Handle repeater-month-name/scalar-year
|
185
44
|
def handle_rmn_sy(tokens, options) #:nodoc:
|
186
45
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
187
46
|
year = tokens[1].get_tag(ScalarYear).type
|
@@ -201,11 +60,13 @@ module Chronic
|
|
201
60
|
end
|
202
61
|
end
|
203
62
|
|
63
|
+
# Handle generic timestamp
|
204
64
|
def handle_rdn_rmn_sd_t_tz_sy(tokens, options) #:nodoc:
|
205
|
-
t = Chronic.time_class.parse(
|
65
|
+
t = Chronic.time_class.parse(options[:text])
|
206
66
|
Span.new(t, t + 1)
|
207
67
|
end
|
208
68
|
|
69
|
+
# Handle repeater-month-name/scalar-day/scalar-year
|
209
70
|
def handle_rmn_sd_sy(tokens, options) #:nodoc:
|
210
71
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
211
72
|
day = tokens[1].get_tag(ScalarDay).type
|
@@ -221,6 +82,7 @@ module Chronic
|
|
221
82
|
end
|
222
83
|
end
|
223
84
|
|
85
|
+
# Handle repeater-month-name/ordinal-day/scalar-year
|
224
86
|
def handle_rmn_od_sy(tokens, options) #:nodoc:
|
225
87
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
226
88
|
day = tokens[1].get_tag(OrdinalDay).type
|
@@ -236,12 +98,14 @@ module Chronic
|
|
236
98
|
end
|
237
99
|
end
|
238
100
|
|
101
|
+
# Handle scalar-day/repeater-month-name/scalar-year
|
239
102
|
def handle_sd_rmn_sy(tokens, options) #:nodoc:
|
240
103
|
new_tokens = [tokens[1], tokens[0], tokens[2]]
|
241
104
|
time_tokens = tokens.last(tokens.size - 3)
|
242
105
|
self.handle_rmn_sd_sy(new_tokens + time_tokens, options)
|
243
106
|
end
|
244
107
|
|
108
|
+
# Handle scalar-month/scalar-day/scalar-year (endian middle)
|
245
109
|
def handle_sm_sd_sy(tokens, options) #:nodoc:
|
246
110
|
month = tokens[0].get_tag(ScalarMonth).type
|
247
111
|
day = tokens[1].get_tag(ScalarDay).type
|
@@ -257,18 +121,21 @@ module Chronic
|
|
257
121
|
end
|
258
122
|
end
|
259
123
|
|
124
|
+
# Handle scalar-day/scalar-month/scalar-year (endian little)
|
260
125
|
def handle_sd_sm_sy(tokens, options) #:nodoc:
|
261
126
|
new_tokens = [tokens[1], tokens[0], tokens[2]]
|
262
127
|
time_tokens = tokens.last(tokens.size - 3)
|
263
128
|
self.handle_sm_sd_sy(new_tokens + time_tokens, options)
|
264
129
|
end
|
265
130
|
|
131
|
+
# Handle scalar-year/scalar-month/scalar-day
|
266
132
|
def handle_sy_sm_sd(tokens, options) #:nodoc:
|
267
133
|
new_tokens = [tokens[1], tokens[2], tokens[0]]
|
268
134
|
time_tokens = tokens.last(tokens.size - 3)
|
269
135
|
self.handle_sm_sd_sy(new_tokens + time_tokens, options)
|
270
136
|
end
|
271
137
|
|
138
|
+
# Handle scalar-month/scalar-year
|
272
139
|
def handle_sm_sy(tokens, options) #:nodoc:
|
273
140
|
month = tokens[0].get_tag(ScalarMonth).type
|
274
141
|
year = tokens[1].get_tag(ScalarYear).type
|
@@ -290,11 +157,13 @@ module Chronic
|
|
290
157
|
|
291
158
|
# anchors
|
292
159
|
|
160
|
+
# Handle repeaters
|
293
161
|
def handle_r(tokens, options) #:nodoc:
|
294
162
|
dd_tokens = dealias_and_disambiguate_times(tokens, options)
|
295
163
|
self.get_anchor(dd_tokens, options)
|
296
164
|
end
|
297
165
|
|
166
|
+
# Handle repeater/grabber/repeater
|
298
167
|
def handle_r_g_r(tokens, options) #:nodoc:
|
299
168
|
new_tokens = [tokens[1], tokens[0], tokens[2]]
|
300
169
|
self.handle_r(new_tokens, options)
|
@@ -302,6 +171,7 @@ module Chronic
|
|
302
171
|
|
303
172
|
# arrows
|
304
173
|
|
174
|
+
# Handle scalar/repeater/pointer helper
|
305
175
|
def handle_srp(tokens, span, options) #:nodoc:
|
306
176
|
distance = tokens[0].get_tag(Scalar).type
|
307
177
|
repeater = tokens[1].get_tag(Repeater)
|
@@ -310,31 +180,21 @@ module Chronic
|
|
310
180
|
repeater.offset(span, distance, pointer)
|
311
181
|
end
|
312
182
|
|
183
|
+
# Handle scalar/repeater/pointer
|
313
184
|
def handle_s_r_p(tokens, options) #:nodoc:
|
314
185
|
repeater = tokens[1].get_tag(Repeater)
|
315
|
-
|
316
|
-
# span =
|
317
|
-
# case true
|
318
|
-
# when [RepeaterYear, RepeaterSeason, RepeaterSeasonName, RepeaterMonth, RepeaterMonthName, RepeaterFortnight, RepeaterWeek].include?(repeater.class)
|
319
|
-
# self.parse("this hour", :guess => false, :now => @now)
|
320
|
-
# when [RepeaterWeekend, RepeaterDay, RepeaterDayName, RepeaterDayPortion, RepeaterHour].include?(repeater.class)
|
321
|
-
# self.parse("this minute", :guess => false, :now => @now)
|
322
|
-
# when [RepeaterMinute, RepeaterSecond].include?(repeater.class)
|
323
|
-
# self.parse("this second", :guess => false, :now => @now)
|
324
|
-
# else
|
325
|
-
# raise(ChronicPain, "Invalid repeater: #{repeater.class}")
|
326
|
-
# end
|
327
|
-
|
328
|
-
span = Span.new(@now, @now + 1)
|
186
|
+
span = Span.new(Chronic.now, Chronic.now + 1)
|
329
187
|
|
330
188
|
self.handle_srp(tokens, span, options)
|
331
189
|
end
|
332
190
|
|
191
|
+
# Handle pointer/scalar/repeater
|
333
192
|
def handle_p_s_r(tokens, options) #:nodoc:
|
334
193
|
new_tokens = [tokens[1], tokens[2], tokens[0]]
|
335
194
|
self.handle_s_r_p(new_tokens, options)
|
336
195
|
end
|
337
196
|
|
197
|
+
# Handle scalar/repeater/pointer/anchor
|
338
198
|
def handle_s_r_p_a(tokens, options) #:nodoc:
|
339
199
|
anchor_span = get_anchor(tokens[3..tokens.size - 1], options)
|
340
200
|
self.handle_srp(tokens, anchor_span, options)
|
@@ -342,6 +202,7 @@ module Chronic
|
|
342
202
|
|
343
203
|
# narrows
|
344
204
|
|
205
|
+
# Handle oridinal repeaters
|
345
206
|
def handle_orr(tokens, outer_span, options) #:nodoc:
|
346
207
|
repeater = tokens[1].get_tag(Repeater)
|
347
208
|
repeater.start = outer_span.begin - 1
|
@@ -357,11 +218,13 @@ module Chronic
|
|
357
218
|
span
|
358
219
|
end
|
359
220
|
|
221
|
+
# Handle ordinal/repeater/separator/repeater
|
360
222
|
def handle_o_r_s_r(tokens, options) #:nodoc:
|
361
223
|
outer_span = get_anchor([tokens[3]], options)
|
362
224
|
handle_orr(tokens[0..1], outer_span, options)
|
363
225
|
end
|
364
226
|
|
227
|
+
# Handle ordinal/repeater/grabber/repeater
|
365
228
|
def handle_o_r_g_r(tokens, options) #:nodoc:
|
366
229
|
outer_span = get_anchor(tokens[2..3], options)
|
367
230
|
handle_orr(tokens[0..1], outer_span, options)
|
@@ -369,6 +232,17 @@ module Chronic
|
|
369
232
|
|
370
233
|
# support methods
|
371
234
|
|
235
|
+
def day_or_time(day_start, time_tokens, options)
|
236
|
+
outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
|
237
|
+
|
238
|
+
if !time_tokens.empty?
|
239
|
+
Chronic.now = outer_span.begin
|
240
|
+
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
|
241
|
+
else
|
242
|
+
outer_span
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
372
246
|
def get_anchor(tokens, options) #:nodoc:
|
373
247
|
grabber = Grabber.new(:this)
|
374
248
|
pointer = :future
|
@@ -382,7 +256,7 @@ module Chronic
|
|
382
256
|
end
|
383
257
|
|
384
258
|
head = repeaters.shift
|
385
|
-
head.start =
|
259
|
+
head.start = Chronic.now
|
386
260
|
|
387
261
|
case grabber.type
|
388
262
|
when :last
|
@@ -404,8 +278,8 @@ module Chronic
|
|
404
278
|
|
405
279
|
def get_repeaters(tokens) #:nodoc:
|
406
280
|
repeaters = []
|
407
|
-
|
408
|
-
|
281
|
+
tokens.each do |token|
|
282
|
+
if t = token.get_tag(Repeater)
|
409
283
|
repeaters << t
|
410
284
|
end
|
411
285
|
end
|
@@ -464,21 +338,6 @@ module Chronic
|
|
464
338
|
end
|
465
339
|
end
|
466
340
|
|
467
|
-
# tokens.each_with_index do |t0, i|
|
468
|
-
# t1 = tokens[i + 1]
|
469
|
-
# if t1 && (t1tag = t1.get_tag(RepeaterDayPortion)) && t0.get_tag(RepeaterTime)
|
470
|
-
# if [:morning].include?(t1tag.type)
|
471
|
-
# puts '--morning->am' if Chronic.debug
|
472
|
-
# t1.untag(RepeaterDayPortion)
|
473
|
-
# t1.tag(RepeaterDayPortion.new(:am))
|
474
|
-
# elsif [:afternoon, :evening, :night].include?(t1tag.type)
|
475
|
-
# puts "--#{t1tag.type}->pm" if Chronic.debug
|
476
|
-
# t1.untag(RepeaterDayPortion)
|
477
|
-
# t1.tag(RepeaterDayPortion.new(:pm))
|
478
|
-
# end
|
479
|
-
# end
|
480
|
-
# end
|
481
|
-
|
482
341
|
# handle ambiguous times if :ambiguous_time_range is specified
|
483
342
|
if options[:ambiguous_time_range] != :none
|
484
343
|
ttokens = []
|
@@ -508,15 +367,14 @@ module Chronic
|
|
508
367
|
end
|
509
368
|
|
510
369
|
def constantize(name)
|
511
|
-
|
512
|
-
::Chronic.module_eval(camel, __FILE__, __LINE__)
|
370
|
+
Chronic.const_get name.to_s.gsub(/(^|_)(.)/) { $2.upcase }
|
513
371
|
end
|
514
372
|
|
515
373
|
def match(tokens, definitions)
|
516
374
|
token_index = 0
|
517
375
|
@pattern.each do |element|
|
518
376
|
name = element.to_s
|
519
|
-
optional = name
|
377
|
+
optional = name[-1, 1] == '?'
|
520
378
|
name = name.chop if optional
|
521
379
|
if element.instance_of? Symbol
|
522
380
|
klass = constantize(name)
|
data/lib/chronic/mini_date.rb
CHANGED
@@ -3,13 +3,17 @@ module Chronic
|
|
3
3
|
attr_accessor :month, :day
|
4
4
|
|
5
5
|
def initialize(month, day)
|
6
|
-
|
6
|
+
unless (1..12).include?(month)
|
7
|
+
raise(InvalidArgumentException, "1..12 are valid months")
|
8
|
+
end
|
9
|
+
|
7
10
|
@month = month
|
8
11
|
@day = day
|
9
12
|
end
|
10
13
|
|
11
14
|
def is_between?(md_start, md_end)
|
12
|
-
return false if (@month==md_start.month && @month==md_end.month &&
|
15
|
+
return false if (@month == md_start.month && @month == md_end.month &&
|
16
|
+
(@day < md_start.day || @day > md_end.day))
|
13
17
|
return true if (@month == md_start.month and @day >= md_start.day) ||
|
14
18
|
(@month == md_end.month and @day <= md_end.day)
|
15
19
|
i = (md_start.month % 12) + 1
|
data/lib/chronic/ordinal.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
module Chronic
|
2
|
-
class Ordinal < Tag
|
2
|
+
class Ordinal < Tag
|
3
|
+
|
4
|
+
# Scan an Array of {Token}s and apply any necessary Ordinal tags to
|
5
|
+
# each token
|
6
|
+
#
|
7
|
+
# @param [Array<Token>] tokens Array of tokens to scan
|
8
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
9
|
+
# @return [Array] list of tokens
|
3
10
|
def self.scan(tokens, options)
|
4
11
|
tokens.each_index do |i|
|
5
12
|
if t = scan_for_ordinals(tokens[i]) then tokens[i].tag(t) end
|
@@ -7,10 +14,14 @@ module Chronic
|
|
7
14
|
end
|
8
15
|
end
|
9
16
|
|
17
|
+
# @param [Token] token
|
18
|
+
# @return [Ordinal, nil]
|
10
19
|
def self.scan_for_ordinals(token)
|
11
20
|
Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
12
21
|
end
|
13
22
|
|
23
|
+
# @param [Token] token
|
24
|
+
# @return [OrdinalDay, nil]
|
14
25
|
def self.scan_for_days(token)
|
15
26
|
if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
16
27
|
unless $1.to_i > 31 || $1.to_i < 1
|