chronic 0.6.3 → 0.10.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 +7 -0
- data/.gitignore +4 -3
- data/.travis.yml +8 -0
- data/HISTORY.md +76 -0
- data/README.md +47 -42
- data/Rakefile +30 -12
- data/chronic.gemspec +10 -4
- data/lib/chronic/date.rb +82 -0
- data/lib/chronic/grabber.rb +9 -7
- data/lib/chronic/handler.rb +47 -40
- data/lib/chronic/handlers.rb +240 -28
- data/lib/chronic/numerizer.rb +11 -2
- data/lib/chronic/ordinal.rb +28 -23
- data/lib/chronic/parser.rb +268 -0
- data/lib/chronic/pointer.rb +9 -7
- data/lib/chronic/repeater.rb +58 -48
- data/lib/chronic/repeaters/repeater_day.rb +4 -3
- data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
- data/lib/chronic/repeaters/repeater_day_portion.rb +33 -18
- data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
- data/lib/chronic/repeaters/repeater_hour.rb +4 -3
- data/lib/chronic/repeaters/repeater_minute.rb +4 -3
- data/lib/chronic/repeaters/repeater_month.rb +5 -4
- data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
- data/lib/chronic/repeaters/repeater_season.rb +5 -3
- data/lib/chronic/repeaters/repeater_second.rb +4 -3
- data/lib/chronic/repeaters/repeater_time.rb +35 -25
- data/lib/chronic/repeaters/repeater_week.rb +4 -3
- data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
- data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
- data/lib/chronic/repeaters/repeater_year.rb +6 -5
- data/lib/chronic/scalar.rb +40 -68
- data/lib/chronic/season.rb +1 -12
- data/lib/chronic/separator.rb +142 -23
- data/lib/chronic/sign.rb +49 -0
- data/lib/chronic/span.rb +2 -2
- data/lib/chronic/tag.rb +10 -15
- data/lib/chronic/time.rb +40 -0
- data/lib/chronic/time_zone.rb +9 -7
- data/lib/chronic/token.rb +16 -10
- data/lib/chronic.rb +109 -70
- data/test/helper.rb +8 -2
- data/test/{test_Chronic.rb → test_chronic.rb} +69 -34
- data/test/{test_DaylightSavings.rb → test_daylight_savings.rb} +1 -1
- data/test/{test_Handler.rb → test_handler.rb} +38 -14
- data/test/{test_MiniDate.rb → test_mini_date.rb} +9 -9
- data/test/{test_Numerizer.rb → test_numerizer.rb} +16 -2
- data/test/test_parsing.rb +392 -22
- data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +1 -1
- data/test/test_repeater_day_portion.rb +254 -0
- data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +1 -1
- data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +1 -1
- data/test/{test_RepeaterMinute.rb → test_repeater_minute.rb} +1 -1
- data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +1 -1
- data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +1 -1
- data/test/{test_RepeaterSeason.rb → test_repeater_season.rb} +1 -1
- data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +19 -1
- data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +1 -1
- data/test/{test_RepeaterWeekday.rb → test_repeater_weekday.rb} +1 -1
- data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +1 -1
- data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +1 -1
- data/test/{test_Span.rb → test_span.rb} +2 -2
- data/test/{test_Token.rb → test_token.rb} +1 -1
- metadata +129 -87
- data/.gemtest +0 -0
- data/.yardopts +0 -3
- data/lib/chronic/chronic.rb +0 -323
data/lib/chronic/handlers.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Chronic
|
|
|
4
4
|
|
|
5
5
|
# Handle month/day
|
|
6
6
|
def handle_m_d(month, day, time_tokens, options)
|
|
7
|
-
month.start =
|
|
7
|
+
month.start = self.now
|
|
8
8
|
span = month.this(options[:context])
|
|
9
9
|
year, month = span.begin.year, span.begin.month
|
|
10
10
|
day_start = Chronic.time_class.local(year, month, day)
|
|
@@ -17,7 +17,7 @@ module Chronic
|
|
|
17
17
|
month = tokens[0].get_tag(RepeaterMonthName)
|
|
18
18
|
day = tokens[1].get_tag(ScalarDay).type
|
|
19
19
|
|
|
20
|
-
return if month_overflow?(
|
|
20
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
21
21
|
|
|
22
22
|
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
23
23
|
end
|
|
@@ -34,7 +34,7 @@ module Chronic
|
|
|
34
34
|
token_range = 0..0
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
return if month_overflow?(
|
|
37
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
38
38
|
|
|
39
39
|
handle_m_d(month, day, tokens[token_range], options)
|
|
40
40
|
end
|
|
@@ -44,17 +44,50 @@ module Chronic
|
|
|
44
44
|
month = tokens[0].get_tag(RepeaterMonthName)
|
|
45
45
|
day = tokens[1].get_tag(OrdinalDay).type
|
|
46
46
|
|
|
47
|
-
return if month_overflow?(
|
|
47
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
48
48
|
|
|
49
49
|
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
# Handle ordinal this month
|
|
53
|
+
def handle_od_rm(tokens, options)
|
|
54
|
+
day = tokens[0].get_tag(OrdinalDay).type
|
|
55
|
+
month = tokens[2].get_tag(RepeaterMonth)
|
|
56
|
+
handle_m_d(month, day, tokens[3..tokens.size], options)
|
|
57
|
+
end
|
|
58
|
+
|
|
52
59
|
# Handle ordinal-day/repeater-month-name
|
|
53
60
|
def handle_od_rmn(tokens, options)
|
|
54
61
|
month = tokens[1].get_tag(RepeaterMonthName)
|
|
55
62
|
day = tokens[0].get_tag(OrdinalDay).type
|
|
56
63
|
|
|
57
|
-
return if month_overflow?(
|
|
64
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
65
|
+
|
|
66
|
+
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def handle_sy_rmn_od(tokens, options)
|
|
70
|
+
year = tokens[0].get_tag(ScalarYear).type
|
|
71
|
+
month = tokens[1].get_tag(RepeaterMonthName).index
|
|
72
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
73
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
74
|
+
|
|
75
|
+
return if month_overflow?(year, month, day)
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
79
|
+
day_or_time(day_start, time_tokens, options)
|
|
80
|
+
rescue ArgumentError
|
|
81
|
+
nil
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Handle scalar-day/repeater-month-name
|
|
86
|
+
def handle_sd_rmn(tokens, options)
|
|
87
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
88
|
+
day = tokens[0].get_tag(ScalarDay).type
|
|
89
|
+
|
|
90
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
58
91
|
|
|
59
92
|
handle_m_d(month, day, tokens[2..tokens.size], options)
|
|
60
93
|
end
|
|
@@ -71,7 +104,7 @@ module Chronic
|
|
|
71
104
|
token_range = 0..0
|
|
72
105
|
end
|
|
73
106
|
|
|
74
|
-
return if month_overflow?(
|
|
107
|
+
return if month_overflow?(self.now.year, month.index, day)
|
|
75
108
|
|
|
76
109
|
handle_m_d(month, day, tokens[token_range], options)
|
|
77
110
|
end
|
|
@@ -98,15 +131,11 @@ module Chronic
|
|
|
98
131
|
end
|
|
99
132
|
|
|
100
133
|
# Handle generic timestamp (ruby 1.8)
|
|
101
|
-
def
|
|
102
|
-
t = Chronic.time_class.parse(options[:text])
|
|
103
|
-
Span.new(t, t + 1)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Handle generic timestamp (ruby 1.9)
|
|
107
|
-
def handle_sy_sm_sd_t_tz(tokens, options)
|
|
134
|
+
def handle_generic(tokens, options)
|
|
108
135
|
t = Chronic.time_class.parse(options[:text])
|
|
109
136
|
Span.new(t, t + 1)
|
|
137
|
+
rescue ArgumentError => e
|
|
138
|
+
raise e unless e.message =~ /out of range/
|
|
110
139
|
end
|
|
111
140
|
|
|
112
141
|
# Handle repeater-month-name/scalar-day/scalar-year
|
|
@@ -198,11 +227,32 @@ module Chronic
|
|
|
198
227
|
handle_sm_sd_sy(new_tokens + time_tokens, options)
|
|
199
228
|
end
|
|
200
229
|
|
|
201
|
-
# Handle scalar-month/scalar-
|
|
202
|
-
def
|
|
230
|
+
# Handle scalar-month/scalar-day
|
|
231
|
+
def handle_sm_sd(tokens, options)
|
|
203
232
|
month = tokens[0].get_tag(ScalarMonth).type
|
|
204
|
-
|
|
233
|
+
day = tokens[1].get_tag(ScalarDay).type
|
|
234
|
+
year = self.now.year
|
|
235
|
+
time_tokens = tokens.last(tokens.size - 2)
|
|
236
|
+
|
|
237
|
+
return if month_overflow?(year, month, day)
|
|
238
|
+
|
|
239
|
+
begin
|
|
240
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
241
|
+
day_start = Chronic.time_class.local(year + 1, month, day) if options[:context] == :future && day_start < now
|
|
242
|
+
day_or_time(day_start, time_tokens, options)
|
|
243
|
+
rescue ArgumentError
|
|
244
|
+
nil
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Handle scalar-day/scalar-month
|
|
249
|
+
def handle_sd_sm(tokens, options)
|
|
250
|
+
new_tokens = [tokens[1], tokens[0]]
|
|
251
|
+
time_tokens = tokens.last(tokens.size - 2)
|
|
252
|
+
handle_sm_sd(new_tokens + time_tokens, options)
|
|
253
|
+
end
|
|
205
254
|
|
|
255
|
+
def handle_year_and_month(year, month)
|
|
206
256
|
if month == 12
|
|
207
257
|
next_month_year = year + 1
|
|
208
258
|
next_month_month = 1
|
|
@@ -219,6 +269,143 @@ module Chronic
|
|
|
219
269
|
end
|
|
220
270
|
end
|
|
221
271
|
|
|
272
|
+
# Handle scalar-month/scalar-year
|
|
273
|
+
def handle_sm_sy(tokens, options)
|
|
274
|
+
month = tokens[0].get_tag(ScalarMonth).type
|
|
275
|
+
year = tokens[1].get_tag(ScalarYear).type
|
|
276
|
+
handle_year_and_month(year, month)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Handle scalar-year/scalar-month
|
|
280
|
+
def handle_sy_sm(tokens, options)
|
|
281
|
+
year = tokens[0].get_tag(ScalarYear).type
|
|
282
|
+
month = tokens[1].get_tag(ScalarMonth).type
|
|
283
|
+
handle_year_and_month(year, month)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Handle RepeaterDayName RepeaterMonthName OrdinalDay
|
|
287
|
+
def handle_rdn_rmn_od(tokens, options)
|
|
288
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
289
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
290
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
291
|
+
year = self.now.year
|
|
292
|
+
|
|
293
|
+
return if month_overflow?(year, month.index, day)
|
|
294
|
+
|
|
295
|
+
begin
|
|
296
|
+
if time_tokens.empty?
|
|
297
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
298
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
299
|
+
Span.new(start_time, end_time)
|
|
300
|
+
else
|
|
301
|
+
day_start = Chronic.time_class.local(year, month.index, day)
|
|
302
|
+
day_or_time(day_start, time_tokens, options)
|
|
303
|
+
end
|
|
304
|
+
rescue ArgumentError
|
|
305
|
+
nil
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Handle RepeaterDayName RepeaterMonthName OrdinalDay ScalarYear
|
|
310
|
+
def handle_rdn_rmn_od_sy(tokens, options)
|
|
311
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
312
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
|
313
|
+
year = tokens[3].get_tag(ScalarYear).type
|
|
314
|
+
|
|
315
|
+
return if month_overflow?(year, month.index, day)
|
|
316
|
+
|
|
317
|
+
begin
|
|
318
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
319
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
320
|
+
Span.new(start_time, end_time)
|
|
321
|
+
rescue ArgumentError
|
|
322
|
+
nil
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Handle RepeaterDayName OrdinalDay
|
|
327
|
+
def handle_rdn_od(tokens, options)
|
|
328
|
+
day = tokens[1].get_tag(OrdinalDay).type
|
|
329
|
+
time_tokens = tokens.last(tokens.size - 2)
|
|
330
|
+
year = self.now.year
|
|
331
|
+
month = self.now.month
|
|
332
|
+
if options[:context] == :future
|
|
333
|
+
self.now.day > day ? month += 1 : month
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
return if month_overflow?(year, month, day)
|
|
337
|
+
|
|
338
|
+
begin
|
|
339
|
+
if time_tokens.empty?
|
|
340
|
+
start_time = Chronic.time_class.local(year, month, day)
|
|
341
|
+
end_time = time_with_rollover(year, month, day + 1)
|
|
342
|
+
Span.new(start_time, end_time)
|
|
343
|
+
else
|
|
344
|
+
day_start = Chronic.time_class.local(year, month, day)
|
|
345
|
+
day_or_time(day_start, time_tokens, options)
|
|
346
|
+
end
|
|
347
|
+
rescue ArgumentError
|
|
348
|
+
nil
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Handle RepeaterDayName RepeaterMonthName ScalarDay
|
|
353
|
+
def handle_rdn_rmn_sd(tokens, options)
|
|
354
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
355
|
+
day = tokens[2].get_tag(ScalarDay).type
|
|
356
|
+
time_tokens = tokens.last(tokens.size - 3)
|
|
357
|
+
year = self.now.year
|
|
358
|
+
|
|
359
|
+
return if month_overflow?(year, month.index, day)
|
|
360
|
+
|
|
361
|
+
begin
|
|
362
|
+
if time_tokens.empty?
|
|
363
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
364
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
365
|
+
Span.new(start_time, end_time)
|
|
366
|
+
else
|
|
367
|
+
day_start = Chronic.time_class.local(year, month.index, day)
|
|
368
|
+
day_or_time(day_start, time_tokens, options)
|
|
369
|
+
end
|
|
370
|
+
rescue ArgumentError
|
|
371
|
+
nil
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# Handle RepeaterDayName RepeaterMonthName ScalarDay ScalarYear
|
|
376
|
+
def handle_rdn_rmn_sd_sy(tokens, options)
|
|
377
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
|
378
|
+
day = tokens[2].get_tag(ScalarDay).type
|
|
379
|
+
year = tokens[3].get_tag(ScalarYear).type
|
|
380
|
+
|
|
381
|
+
return if month_overflow?(year, month.index, day)
|
|
382
|
+
|
|
383
|
+
begin
|
|
384
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
|
385
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
|
386
|
+
Span.new(start_time, end_time)
|
|
387
|
+
rescue ArgumentError
|
|
388
|
+
nil
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def handle_sm_rmn_sy(tokens, options)
|
|
393
|
+
day = tokens[0].get_tag(ScalarDay).type
|
|
394
|
+
month = tokens[1].get_tag(RepeaterMonthName).index
|
|
395
|
+
year = tokens[2].get_tag(ScalarYear).type
|
|
396
|
+
if tokens.size > 3
|
|
397
|
+
time = get_anchor([tokens.last], options).begin
|
|
398
|
+
h, m, s = time.hour, time.min, time.sec
|
|
399
|
+
time = Chronic.time_class.local(year, month, day, h, m, s)
|
|
400
|
+
end_time = Chronic.time_class.local(year, month, day + 1, h, m, s)
|
|
401
|
+
else
|
|
402
|
+
time = Chronic.time_class.local(year, month, day)
|
|
403
|
+
day += 1 unless day >= 31
|
|
404
|
+
end_time = Chronic.time_class.local(year, month, day)
|
|
405
|
+
end
|
|
406
|
+
Span.new(time, end_time)
|
|
407
|
+
end
|
|
408
|
+
|
|
222
409
|
# anchors
|
|
223
410
|
|
|
224
411
|
# Handle repeaters
|
|
@@ -241,13 +428,12 @@ module Chronic
|
|
|
241
428
|
repeater = tokens[1].get_tag(Repeater)
|
|
242
429
|
pointer = tokens[2].get_tag(Pointer).type
|
|
243
430
|
|
|
244
|
-
repeater.offset(span, distance, pointer)
|
|
431
|
+
repeater.offset(span, distance, pointer) if repeater.respond_to?(:offset)
|
|
245
432
|
end
|
|
246
433
|
|
|
247
434
|
# Handle scalar/repeater/pointer
|
|
248
435
|
def handle_s_r_p(tokens, options)
|
|
249
|
-
|
|
250
|
-
span = Span.new(Chronic.now, Chronic.now + 1)
|
|
436
|
+
span = Span.new(self.now, self.now + 1)
|
|
251
437
|
|
|
252
438
|
handle_srp(tokens, span, options)
|
|
253
439
|
end
|
|
@@ -264,6 +450,13 @@ module Chronic
|
|
|
264
450
|
handle_srp(tokens, anchor_span, options)
|
|
265
451
|
end
|
|
266
452
|
|
|
453
|
+
def handle_s_r_a_s_r_p_a(tokens, options)
|
|
454
|
+
anchor_span = get_anchor(tokens[4..tokens.size - 1], options)
|
|
455
|
+
|
|
456
|
+
span = handle_srp(tokens[0..1]+tokens[4..6], anchor_span, options)
|
|
457
|
+
handle_srp(tokens[2..3]+tokens[4..6], span, options)
|
|
458
|
+
end
|
|
459
|
+
|
|
267
460
|
# narrows
|
|
268
461
|
|
|
269
462
|
# Handle oridinal repeaters
|
|
@@ -276,7 +469,7 @@ module Chronic
|
|
|
276
469
|
ordinal.times do
|
|
277
470
|
span = repeater.next(:future)
|
|
278
471
|
|
|
279
|
-
if span.begin
|
|
472
|
+
if span.begin >= outer_span.end
|
|
280
473
|
span = nil
|
|
281
474
|
break
|
|
282
475
|
end
|
|
@@ -302,9 +495,9 @@ module Chronic
|
|
|
302
495
|
def day_or_time(day_start, time_tokens, options)
|
|
303
496
|
outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
|
|
304
497
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
|
|
498
|
+
unless time_tokens.empty?
|
|
499
|
+
self.now = outer_span.begin
|
|
500
|
+
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options.merge(:context => :future))
|
|
308
501
|
else
|
|
309
502
|
outer_span
|
|
310
503
|
end
|
|
@@ -313,7 +506,6 @@ module Chronic
|
|
|
313
506
|
def get_anchor(tokens, options)
|
|
314
507
|
grabber = Grabber.new(:this)
|
|
315
508
|
pointer = :future
|
|
316
|
-
|
|
317
509
|
repeaters = get_repeaters(tokens)
|
|
318
510
|
repeaters.size.times { tokens.pop }
|
|
319
511
|
|
|
@@ -322,7 +514,7 @@ module Chronic
|
|
|
322
514
|
end
|
|
323
515
|
|
|
324
516
|
head = repeaters.shift
|
|
325
|
-
head.start =
|
|
517
|
+
head.start = self.now
|
|
326
518
|
|
|
327
519
|
case grabber.type
|
|
328
520
|
when :last
|
|
@@ -336,10 +528,14 @@ module Chronic
|
|
|
336
528
|
when :next
|
|
337
529
|
outer_span = head.next(:future)
|
|
338
530
|
else
|
|
339
|
-
raise
|
|
531
|
+
raise "Invalid grabber"
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
if Chronic.debug
|
|
535
|
+
puts "Handler-class: #{head.class}"
|
|
536
|
+
puts "--#{outer_span}"
|
|
340
537
|
end
|
|
341
538
|
|
|
342
|
-
puts "--#{outer_span}" if Chronic.debug
|
|
343
539
|
find_within(repeaters, outer_span, pointer)
|
|
344
540
|
end
|
|
345
541
|
|
|
@@ -348,11 +544,13 @@ module Chronic
|
|
|
348
544
|
end
|
|
349
545
|
|
|
350
546
|
def month_overflow?(year, month, day)
|
|
351
|
-
if Date.leap?(year)
|
|
547
|
+
if ::Date.leap?(year)
|
|
352
548
|
day > RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
|
|
353
549
|
else
|
|
354
550
|
day > RepeaterMonth::MONTH_DAYS[month - 1]
|
|
355
551
|
end
|
|
552
|
+
rescue ArgumentError
|
|
553
|
+
false
|
|
356
554
|
end
|
|
357
555
|
|
|
358
556
|
# Recursively finds repeaters within other repeaters.
|
|
@@ -371,6 +569,20 @@ module Chronic
|
|
|
371
569
|
end
|
|
372
570
|
end
|
|
373
571
|
|
|
572
|
+
def time_with_rollover(year, month, day)
|
|
573
|
+
date_parts =
|
|
574
|
+
if month_overflow?(year, month, day)
|
|
575
|
+
if month == 12
|
|
576
|
+
[year + 1, 1, 1]
|
|
577
|
+
else
|
|
578
|
+
[year, month + 1, 1]
|
|
579
|
+
end
|
|
580
|
+
else
|
|
581
|
+
[year, month, day]
|
|
582
|
+
end
|
|
583
|
+
Chronic.time_class.local(*date_parts)
|
|
584
|
+
end
|
|
585
|
+
|
|
374
586
|
def dealias_and_disambiguate_times(tokens, options)
|
|
375
587
|
# handle aliases of am/pm
|
|
376
588
|
# 5:00 in the morning -> 5:00 am
|
data/lib/chronic/numerizer.rb
CHANGED
|
@@ -37,7 +37,16 @@ module Chronic
|
|
|
37
37
|
['seventh', '7'],
|
|
38
38
|
['eighth', '8'],
|
|
39
39
|
['ninth', '9'],
|
|
40
|
-
['tenth', '10']
|
|
40
|
+
['tenth', '10'],
|
|
41
|
+
['twelfth', '12'],
|
|
42
|
+
['twentieth', '20'],
|
|
43
|
+
['thirtieth', '30'],
|
|
44
|
+
['fourtieth', '40'],
|
|
45
|
+
['fiftieth', '50'],
|
|
46
|
+
['sixtieth', '60'],
|
|
47
|
+
['seventieth', '70'],
|
|
48
|
+
['eightieth', '80'],
|
|
49
|
+
['ninetieth', '90']
|
|
41
50
|
]
|
|
42
51
|
|
|
43
52
|
TEN_PREFIXES = [
|
|
@@ -90,7 +99,7 @@ module Chronic
|
|
|
90
99
|
# hundreds, thousands, millions, etc.
|
|
91
100
|
|
|
92
101
|
BIG_PREFIXES.each do |bp|
|
|
93
|
-
string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { '<num>' + (bp[1] * $1.to_i).to_s}
|
|
102
|
+
string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { $1.empty? ? bp[1] : '<num>' + (bp[1] * $1.to_i).to_s}
|
|
94
103
|
andition(string)
|
|
95
104
|
end
|
|
96
105
|
|
data/lib/chronic/ordinal.rb
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
module Chronic
|
|
2
2
|
class Ordinal < Tag
|
|
3
3
|
|
|
4
|
-
# Scan an Array of
|
|
5
|
-
# each token
|
|
4
|
+
# Scan an Array of Token objects and apply any necessary Ordinal
|
|
5
|
+
# tags to each token.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
7
|
+
# tokens - An Array of tokens to scan.
|
|
8
|
+
# options - The Hash of options specified in Chronic::parse.
|
|
9
|
+
#
|
|
10
|
+
# Returns an Array of tokens.
|
|
10
11
|
def self.scan(tokens, options)
|
|
11
|
-
tokens.
|
|
12
|
-
if
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# @param [Token] token
|
|
24
|
-
# @return [OrdinalDay, nil]
|
|
25
|
-
def self.scan_for_days(token)
|
|
26
|
-
if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
|
27
|
-
unless $1.to_i > 31 || $1.to_i < 1
|
|
28
|
-
OrdinalDay.new(token.word.to_i)
|
|
12
|
+
tokens.each_index do |i|
|
|
13
|
+
if tokens[i].word =~ /^(\d+)(st|nd|rd|th|\.)$/
|
|
14
|
+
ordinal = $1.to_i
|
|
15
|
+
tokens[i].tag(Ordinal.new(ordinal))
|
|
16
|
+
tokens[i].tag(OrdinalDay.new(ordinal)) if Chronic::Date::could_be_day?(ordinal)
|
|
17
|
+
tokens[i].tag(OrdinalMonth.new(ordinal)) if Chronic::Date::could_be_month?(ordinal)
|
|
18
|
+
if Chronic::Date::could_be_year?(ordinal)
|
|
19
|
+
year = Chronic::Date::make_year(ordinal, options[:ambiguous_year_future_bias])
|
|
20
|
+
tokens[i].tag(OrdinalYear.new(year.to_i))
|
|
21
|
+
end
|
|
29
22
|
end
|
|
30
23
|
end
|
|
31
24
|
end
|
|
@@ -41,4 +34,16 @@ module Chronic
|
|
|
41
34
|
end
|
|
42
35
|
end
|
|
43
36
|
|
|
37
|
+
class OrdinalMonth < Ordinal #:nodoc:
|
|
38
|
+
def to_s
|
|
39
|
+
super << '-month-' << @type.to_s
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class OrdinalYear < Ordinal #:nodoc:
|
|
44
|
+
def to_s
|
|
45
|
+
super << '-year-' << @type.to_s
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
44
49
|
end
|