gitlab-chronic 0.10.4 → 0.10.5
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/.gitignore +0 -1
- data/.travis.yml +4 -6
- data/HISTORY.md +4 -0
- data/README.md +7 -10
- data/chronic.gemspec +1 -1
- data/lib/chronic/date.rb +7 -6
- data/lib/chronic/{tags/grabber.rb → grabber.rb} +10 -6
- data/lib/chronic/handlers.rb +2 -27
- data/lib/chronic/mini_date.rb +2 -2
- data/lib/chronic/numerizer.rb +130 -0
- data/lib/chronic/{tags/ordinal.rb → ordinal.rb} +8 -11
- data/lib/chronic/parser.rb +80 -34
- data/lib/chronic/{tags/pointer.rb → pointer.rb} +9 -5
- data/lib/chronic/{tags/repeater.rb → repeater.rb} +11 -26
- data/lib/chronic/repeaters/repeater_day.rb +1 -1
- data/lib/chronic/repeaters/repeater_day_name.rb +2 -2
- data/lib/chronic/repeaters/repeater_day_portion.rb +3 -3
- data/lib/chronic/repeaters/repeater_fortnight.rb +1 -1
- data/lib/chronic/repeaters/repeater_hour.rb +1 -1
- data/lib/chronic/repeaters/repeater_minute.rb +1 -1
- data/lib/chronic/repeaters/repeater_month.rb +1 -1
- data/lib/chronic/repeaters/repeater_month_name.rb +2 -2
- data/lib/chronic/repeaters/repeater_season.rb +1 -1
- data/lib/chronic/repeaters/repeater_second.rb +1 -1
- data/lib/chronic/repeaters/repeater_time.rb +2 -2
- data/lib/chronic/repeaters/repeater_week.rb +22 -23
- data/lib/chronic/repeaters/repeater_weekday.rb +2 -2
- data/lib/chronic/repeaters/repeater_weekend.rb +1 -1
- data/lib/chronic/repeaters/repeater_year.rb +1 -1
- data/lib/chronic/{tags/scalar.rb → scalar.rb} +10 -18
- data/lib/chronic/separator.rb +207 -0
- data/lib/chronic/{tags/sign.rb → sign.rb} +16 -2
- data/lib/chronic/tag.rb +7 -59
- data/lib/chronic/time.rb +8 -8
- data/lib/chronic/{tags/time_zone.rb → time_zone.rb} +1 -1
- data/lib/chronic/token.rb +3 -13
- data/lib/chronic/version.rb +1 -1
- data/lib/gitlab-chronic.rb +14 -18
- data/test/test_chronic.rb +6 -26
- data/test/test_handler.rb +1 -1
- data/test/test_numerizer.rb +86 -0
- data/test/test_parsing.rb +8 -306
- data/test/test_repeater_week.rb +0 -53
- data/test/test_token.rb +0 -6
- metadata +13 -19
- data/lib/chronic/definition.rb +0 -128
- data/lib/chronic/dictionary.rb +0 -36
- data/lib/chronic/repeaters/repeater_quarter.rb +0 -59
- data/lib/chronic/repeaters/repeater_quarter_name.rb +0 -40
- data/lib/chronic/tags/separator.rb +0 -123
- data/lib/chronic/tokenizer.rb +0 -38
- data/test/test_repeater_quarter.rb +0 -70
- data/test/test_repeater_quarter_name.rb +0 -198
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b5a569ed8fd73d35d0726a7cb2f19f1ca3507543032bff4ca3f1aa73e1ba94f
|
4
|
+
data.tar.gz: 15f9949677fc421bf44def4fbf0da85928847e4918d4375657cf43c64f3f92fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8ae6e9b8292042fce47b1125f2fd11aab058e45c5ab00fe01fdee2e8b8dddcc062fd505f1de80bc7a05fcc04c329e3229746bcbfaaa07b5d3d82ad7161a1a09
|
7
|
+
data.tar.gz: 727012bcb6c52dcd35e3f87ef099d50e27d7ab7b7c719a8f21d25e4ab802c06b3aa70a1acb790fa376efb2851d2c2d8e03c33539e3c03e51f33b90f04893044f
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/HISTORY.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
## --- Begin gitlab-chronic changes ---
|
2
2
|
|
3
|
+
# 0.10.5 / 2019-11-25
|
4
|
+
|
5
|
+
* Revert changes made between v0.10.2..master ((https://gitlab.com/gitlab-org/gitlab-chronic/merge_requests/7)
|
6
|
+
|
3
7
|
# 0.10.4 / 2019-11-24
|
4
8
|
|
5
9
|
* Rename chronic -> gitlab-chronic (https://gitlab.com/gitlab-org/gitlab-chronic/merge_requests/5)
|
data/README.md
CHANGED
@@ -57,7 +57,7 @@ Chronic can parse a huge variety of date and time formats. Following is a
|
|
57
57
|
small sample of strings that will be properly parsed. Parsing is case
|
58
58
|
insensitive and will handle common abbreviations and misspellings.
|
59
59
|
|
60
|
-
|
60
|
+
Simple
|
61
61
|
|
62
62
|
* thursday
|
63
63
|
* november
|
@@ -89,7 +89,7 @@ insensitive and will handle common abbreviations and misspellings.
|
|
89
89
|
* afternoon yesterday
|
90
90
|
* thursday last week
|
91
91
|
|
92
|
-
|
92
|
+
Complex
|
93
93
|
|
94
94
|
* 3 years ago
|
95
95
|
* a year ago
|
@@ -108,7 +108,7 @@ insensitive and will handle common abbreviations and misspellings.
|
|
108
108
|
* fourteenth of june 2010 at eleven o'clock in the evening
|
109
109
|
* may seventh '97 at three in the morning
|
110
110
|
|
111
|
-
|
111
|
+
Specific Dates
|
112
112
|
|
113
113
|
* January 5
|
114
114
|
* 22nd of june
|
@@ -133,14 +133,12 @@ insensitive and will handle common abbreviations and misspellings.
|
|
133
133
|
* 17:00
|
134
134
|
* 0800
|
135
135
|
|
136
|
-
|
136
|
+
Specific Times (many of the above with an added time)
|
137
137
|
|
138
138
|
* January 5 at 7pm
|
139
139
|
* 22nd of june at 8am
|
140
140
|
* 1979-05-27 05:00:00
|
141
141
|
* 03/01/2012 07:25:09.234567
|
142
|
-
* 2013-08-01T19:30:00.345-07:00
|
143
|
-
* 2013-08-01T19:30:00.34-07:00
|
144
142
|
* etc
|
145
143
|
|
146
144
|
|
@@ -163,7 +161,7 @@ class to get full time zone support.
|
|
163
161
|
|
164
162
|
Chronic uses Ruby's built in Time class for all time storage and computation.
|
165
163
|
Because of this, only times that the Time class can handle will be properly
|
166
|
-
parsed. Parsing for times outside of this range will simply return
|
164
|
+
parsed. Parsing for times outside of this range will simply return nil.
|
167
165
|
Support for a wider range of times is planned for a future release.
|
168
166
|
|
169
167
|
|
@@ -177,10 +175,9 @@ The best way to get your changes merged back into core is as follows:
|
|
177
175
|
|
178
176
|
1. Clone down your fork
|
179
177
|
1. Create a thoughtfully named topic branch to contain your change
|
180
|
-
1. Install the development dependencies by running `bundle install`
|
181
178
|
1. Hack away
|
182
|
-
1. Add tests and make sure everything still passes by running `
|
183
|
-
1. Ensure your tests pass in multiple timezones. ie `TZ=utc
|
179
|
+
1. Add tests and make sure everything still passes by running `rake`
|
180
|
+
1. Ensure your tests pass in multiple timezones. ie `TZ=utc rake` `TZ=BST rake`
|
184
181
|
1. If you are adding new functionality, document it in the README
|
185
182
|
1. Do not change the version number, we will do that on our end
|
186
183
|
1. If necessary, rebase your commits into logical chunks, without errors
|
data/chronic.gemspec
CHANGED
data/lib/chronic/date.rb
CHANGED
@@ -35,19 +35,20 @@ module Chronic
|
|
35
35
|
}
|
36
36
|
|
37
37
|
# Checks if given number could be day
|
38
|
-
def self.could_be_day?(day
|
39
|
-
day >= 1 && day <= 31
|
38
|
+
def self.could_be_day?(day)
|
39
|
+
day >= 1 && day <= 31
|
40
40
|
end
|
41
41
|
|
42
42
|
# Checks if given number could be month
|
43
|
-
def self.could_be_month?(month
|
44
|
-
month >= 1 && month <= 12
|
43
|
+
def self.could_be_month?(month)
|
44
|
+
month >= 1 && month <= 12
|
45
45
|
end
|
46
46
|
|
47
47
|
# Checks if given number could be year
|
48
|
-
def self.could_be_year?(year
|
49
|
-
year >=
|
48
|
+
def self.could_be_year?(year)
|
49
|
+
year >= 1 && year <= 9999
|
50
50
|
end
|
51
|
+
|
51
52
|
# Build a year from a 2 digit suffix.
|
52
53
|
#
|
53
54
|
# year - The two digit Integer year to build from.
|
@@ -10,15 +10,19 @@ module Chronic
|
|
10
10
|
# Returns an Array of Token objects.
|
11
11
|
def self.scan(tokens, options)
|
12
12
|
tokens.each do |token|
|
13
|
-
token.tag
|
13
|
+
if t = scan_for_all(token) then token.tag(t); next end
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
# token - The Token object to scan.
|
18
|
+
#
|
19
|
+
# Returns a new Grabber object.
|
20
|
+
def self.scan_for_all(token)
|
21
|
+
scan_for token, self,
|
22
|
+
{
|
23
|
+
/last/ => :last,
|
24
|
+
/this/ => :this,
|
25
|
+
/next/ => :next
|
22
26
|
}
|
23
27
|
end
|
24
28
|
|
data/lib/chronic/handlers.rb
CHANGED
@@ -8,7 +8,6 @@ module Chronic
|
|
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)
|
11
|
-
day_start = Chronic.time_class.local(year + 1, month, day) if options[:context] == :future && day_start < now
|
12
11
|
|
13
12
|
day_or_time(day_start, time_tokens, options)
|
14
13
|
end
|
@@ -110,19 +109,6 @@ module Chronic
|
|
110
109
|
handle_m_d(month, day, tokens[token_range], options)
|
111
110
|
end
|
112
111
|
|
113
|
-
# Handle scalar-year/repeater-quarter-name
|
114
|
-
def handle_sy_rqn(tokens, options)
|
115
|
-
handle_rqn_sy(tokens[0..1].reverse, options)
|
116
|
-
end
|
117
|
-
|
118
|
-
# Handle repeater-quarter-name/scalar-year
|
119
|
-
def handle_rqn_sy(tokens, options)
|
120
|
-
year = tokens[1].get_tag(ScalarYear).type
|
121
|
-
quarter_tag = tokens[0].get_tag(RepeaterQuarterName)
|
122
|
-
quarter_tag.start = Chronic.construct(year)
|
123
|
-
quarter_tag.this(:none)
|
124
|
-
end
|
125
|
-
|
126
112
|
# Handle repeater-month-name/scalar-year
|
127
113
|
def handle_rmn_sy(tokens, options)
|
128
114
|
month = tokens[0].get_tag(RepeaterMonthName).index
|
@@ -252,13 +238,7 @@ module Chronic
|
|
252
238
|
|
253
239
|
begin
|
254
240
|
day_start = Chronic.time_class.local(year, month, day)
|
255
|
-
|
256
|
-
if options[:context] == :future && day_start < now
|
257
|
-
day_start = Chronic.time_class.local(year + 1, month, day)
|
258
|
-
elsif options[:context] == :past && day_start > now
|
259
|
-
day_start = Chronic.time_class.local(year - 1, month, day)
|
260
|
-
end
|
261
|
-
|
241
|
+
day_start = Chronic.time_class.local(year + 1, month, day) if options[:context] == :future && day_start < now
|
262
242
|
day_or_time(day_start, time_tokens, options)
|
263
243
|
rescue ArgumentError
|
264
244
|
nil
|
@@ -470,11 +450,6 @@ module Chronic
|
|
470
450
|
handle_srp(tokens, anchor_span, options)
|
471
451
|
end
|
472
452
|
|
473
|
-
# Handle repeater/scalar/repeater/pointer
|
474
|
-
def handle_rmn_s_r_p(tokens, options)
|
475
|
-
handle_s_r_p_a(tokens[1..3] + tokens[0..0], options)
|
476
|
-
end
|
477
|
-
|
478
453
|
def handle_s_r_a_s_r_p_a(tokens, options)
|
479
454
|
anchor_span = get_anchor(tokens[4..tokens.size - 1], options)
|
480
455
|
|
@@ -553,7 +528,7 @@ module Chronic
|
|
553
528
|
when :next
|
554
529
|
outer_span = head.next(:future)
|
555
530
|
else
|
556
|
-
raise
|
531
|
+
raise "Invalid grabber"
|
557
532
|
end
|
558
533
|
|
559
534
|
if Chronic.debug
|
data/lib/chronic/mini_date.rb
CHANGED
@@ -8,7 +8,7 @@ module Chronic
|
|
8
8
|
|
9
9
|
def initialize(month, day)
|
10
10
|
unless (1..12).include?(month)
|
11
|
-
raise ArgumentError,
|
11
|
+
raise ArgumentError, "1..12 are valid months"
|
12
12
|
end
|
13
13
|
|
14
14
|
@month = month
|
@@ -35,4 +35,4 @@ module Chronic
|
|
35
35
|
@month == other.month and @day == other.day
|
36
36
|
end
|
37
37
|
end
|
38
|
-
end
|
38
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
module Chronic
|
4
|
+
class Numerizer
|
5
|
+
|
6
|
+
DIRECT_NUMS = [
|
7
|
+
['eleven', '11'],
|
8
|
+
['twelve', '12'],
|
9
|
+
['thirteen', '13'],
|
10
|
+
['fourteen', '14'],
|
11
|
+
['fifteen', '15'],
|
12
|
+
['sixteen', '16'],
|
13
|
+
['seventeen', '17'],
|
14
|
+
['eighteen', '18'],
|
15
|
+
['nineteen', '19'],
|
16
|
+
['ninteen', '19'], # Common mis-spelling
|
17
|
+
['zero', '0'],
|
18
|
+
['one', '1'],
|
19
|
+
['two', '2'],
|
20
|
+
['three', '3'],
|
21
|
+
['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
|
22
|
+
['five', '5'],
|
23
|
+
['six(\W|$)', '6\1'],
|
24
|
+
['seven(\W|$)', '7\1'],
|
25
|
+
['eight(\W|$)', '8\1'],
|
26
|
+
['nine(\W|$)', '9\1'],
|
27
|
+
['ten', '10'],
|
28
|
+
['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
|
29
|
+
]
|
30
|
+
|
31
|
+
ORDINALS = [
|
32
|
+
['first', '1'],
|
33
|
+
['third', '3'],
|
34
|
+
['fourth', '4'],
|
35
|
+
['fifth', '5'],
|
36
|
+
['sixth', '6'],
|
37
|
+
['seventh', '7'],
|
38
|
+
['eighth', '8'],
|
39
|
+
['ninth', '9'],
|
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']
|
50
|
+
]
|
51
|
+
|
52
|
+
TEN_PREFIXES = [
|
53
|
+
['twenty', 20],
|
54
|
+
['thirty', 30],
|
55
|
+
['forty', 40],
|
56
|
+
['fourty', 40], # Common mis-spelling
|
57
|
+
['fifty', 50],
|
58
|
+
['sixty', 60],
|
59
|
+
['seventy', 70],
|
60
|
+
['eighty', 80],
|
61
|
+
['ninety', 90]
|
62
|
+
]
|
63
|
+
|
64
|
+
BIG_PREFIXES = [
|
65
|
+
['hundred', 100],
|
66
|
+
['thousand', 1000],
|
67
|
+
['million', 1_000_000],
|
68
|
+
['billion', 1_000_000_000],
|
69
|
+
['trillion', 1_000_000_000_000],
|
70
|
+
]
|
71
|
+
|
72
|
+
def self.numerize(string)
|
73
|
+
string = string.dup
|
74
|
+
|
75
|
+
# preprocess
|
76
|
+
string.gsub!(/ +|([^\d])-([^\d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
|
77
|
+
string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
|
78
|
+
|
79
|
+
# easy/direct replacements
|
80
|
+
|
81
|
+
DIRECT_NUMS.each do |dn|
|
82
|
+
string.gsub!(/#{dn[0]}/i, '<num>' + dn[1])
|
83
|
+
end
|
84
|
+
|
85
|
+
ORDINALS.each do |on|
|
86
|
+
string.gsub!(/#{on[0]}/i, '<num>' + on[1] + on[0][-2, 2])
|
87
|
+
end
|
88
|
+
|
89
|
+
# ten, twenty, etc.
|
90
|
+
|
91
|
+
TEN_PREFIXES.each do |tp|
|
92
|
+
string.gsub!(/(?:#{tp[0]}) *<num>(\d(?=[^\d]|$))*/i) { '<num>' + (tp[1] + $1.to_i).to_s }
|
93
|
+
end
|
94
|
+
|
95
|
+
TEN_PREFIXES.each do |tp|
|
96
|
+
string.gsub!(/#{tp[0]}/i) { '<num>' + tp[1].to_s }
|
97
|
+
end
|
98
|
+
|
99
|
+
# hundreds, thousands, millions, etc.
|
100
|
+
|
101
|
+
BIG_PREFIXES.each do |bp|
|
102
|
+
string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { $1.empty? ? bp[1] : '<num>' + (bp[1] * $1.to_i).to_s}
|
103
|
+
andition(string)
|
104
|
+
end
|
105
|
+
|
106
|
+
# fractional addition
|
107
|
+
# I'm not combining this with the previous block as using float addition complicates the strings
|
108
|
+
# (with extraneous .0's and such )
|
109
|
+
string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
|
110
|
+
|
111
|
+
string.gsub(/<num>/, '')
|
112
|
+
end
|
113
|
+
|
114
|
+
class << self
|
115
|
+
private
|
116
|
+
|
117
|
+
def andition(string)
|
118
|
+
sc = StringScanner.new(string)
|
119
|
+
|
120
|
+
while sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i)
|
121
|
+
if sc[2] =~ /and/ || sc[1].size > sc[3].size
|
122
|
+
string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
|
123
|
+
sc.reset
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -11,17 +11,14 @@ module Chronic
|
|
11
11
|
def self.scan(tokens, options)
|
12
12
|
tokens.each_index do |i|
|
13
13
|
if tokens[i].word =~ /^(\d+)(st|nd|rd|th|\.)$/
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
elsif tokens[i].word =~ /^second$/
|
24
|
-
tokens[i].tag(Ordinal.new(2, 1))
|
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
|
25
22
|
end
|
26
23
|
end
|
27
24
|
end
|
data/lib/chronic/parser.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'chronic/dictionary'
|
2
1
|
require 'chronic/handlers'
|
3
2
|
|
4
3
|
module Chronic
|
@@ -10,7 +9,6 @@ module Chronic
|
|
10
9
|
:context => :future,
|
11
10
|
:now => nil,
|
12
11
|
:hours24 => nil,
|
13
|
-
:week_start => :sunday,
|
14
12
|
:guess => true,
|
15
13
|
:ambiguous_time_range => 6,
|
16
14
|
:endian_precedence => [:middle, :little],
|
@@ -27,9 +25,6 @@ module Chronic
|
|
27
25
|
# :now - Time, all computations will be based off of time
|
28
26
|
# instead of Time.now.
|
29
27
|
# :hours24 - Time will be parsed as it would be 24 hour clock.
|
30
|
-
# :week_start - By default, the parser assesses weeks start on
|
31
|
-
# sunday but you can change this value to :monday if
|
32
|
-
# needed.
|
33
28
|
# :guess - By default the parser will guess a single point in time
|
34
29
|
# for the given date or time. If you'd rather have the
|
35
30
|
# entire time span returned, set this to false
|
@@ -55,9 +50,8 @@ module Chronic
|
|
55
50
|
# two digit year is `now + x years` it's assumed to be the
|
56
51
|
# future, `now - x years` is assumed to be the past.
|
57
52
|
def initialize(options = {})
|
58
|
-
validate_options!(options)
|
59
53
|
@options = DEFAULT_OPTIONS.merge(options)
|
60
|
-
@now = options
|
54
|
+
@now = options.delete(:now) || Chronic.time_class.now
|
61
55
|
end
|
62
56
|
|
63
57
|
# Parse "text" with the given options
|
@@ -94,35 +88,22 @@ module Chronic
|
|
94
88
|
# Returns a new String ready for Chronic to parse.
|
95
89
|
def pre_normalize(text)
|
96
90
|
text = text.to_s.downcase
|
97
|
-
text.gsub!(/\b(\d{
|
91
|
+
text.gsub!(/\b(\d{2})\.(\d{2})\.(\d{4})\b/, '\3 / \2 / \1')
|
98
92
|
text.gsub!(/\b([ap])\.m\.?/, '\1m')
|
99
|
-
text.gsub!(/(\s+|:\d{2}|:\d{2}\.\d
|
93
|
+
text.gsub!(/(\s+|:\d{2}|:\d{2}\.\d{3})\-(\d{2}:?\d{2})\b/, '\1tzminus\2')
|
100
94
|
text.gsub!(/\./, ':')
|
101
95
|
text.gsub!(/([ap]):m:?/, '\1m')
|
102
|
-
text.gsub!(/'(\d{2})\b/) do
|
103
|
-
number = $1.to_i
|
104
|
-
|
105
|
-
if Chronic::Date::could_be_year?(number)
|
106
|
-
Chronic::Date::make_year(number, options[:ambiguous_year_future_bias])
|
107
|
-
else
|
108
|
-
number
|
109
|
-
end
|
110
|
-
end
|
111
96
|
text.gsub!(/['"]/, '')
|
112
97
|
text.gsub!(/,/, ' ')
|
113
98
|
text.gsub!(/^second /, '2nd ')
|
114
|
-
text.gsub!(/\bsecond (of|day|month|hour|minute|second
|
115
|
-
text.gsub!(/\bthird quarter\b/, '3rd q')
|
116
|
-
text.gsub!(/\bfourth quarter\b/, '4th q')
|
117
|
-
text.gsub!(/quarters?(\s+|$)(?!to|till|past|after|before)/, 'q\1')
|
99
|
+
text.gsub!(/\bsecond (of|day|month|hour|minute|second)\b/, '2nd \1')
|
118
100
|
text = Numerizer.numerize(text)
|
119
|
-
text.gsub!(/\b(\d)(?:st|nd|rd|th)\s+q\b/, 'q\1')
|
120
101
|
text.gsub!(/([\/\-\,\@])/) { ' ' + $1 + ' ' }
|
121
102
|
text.gsub!(/(?:^|\s)0(\d+:\d+\s*pm?\b)/, ' \1')
|
122
103
|
text.gsub!(/\btoday\b/, 'this day')
|
123
104
|
text.gsub!(/\btomm?orr?ow\b/, 'next day')
|
124
105
|
text.gsub!(/\byesterday\b/, 'last day')
|
125
|
-
text.gsub!(/\bnoon
|
106
|
+
text.gsub!(/\bnoon\b/, '12:00pm')
|
126
107
|
text.gsub!(/\bmidnight\b/, '24:00')
|
127
108
|
text.gsub!(/\bnow\b/, 'this second')
|
128
109
|
text.gsub!('quarter', '15')
|
@@ -163,22 +144,87 @@ module Chronic
|
|
163
144
|
#
|
164
145
|
# Returns a Hash of Handler definitions.
|
165
146
|
def definitions(options = {})
|
166
|
-
|
167
|
-
end
|
147
|
+
options[:endian_precedence] ||= [:middle, :little]
|
168
148
|
|
169
|
-
|
149
|
+
@@definitions ||= {
|
150
|
+
:time => [
|
151
|
+
Handler.new([:repeater_time, :repeater_day_portion?], nil)
|
152
|
+
],
|
153
|
+
|
154
|
+
:date => [
|
155
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, [:separator_slash?, :separator_dash?], :time_zone, :scalar_year], :handle_generic),
|
156
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day], :handle_rdn_rmn_sd),
|
157
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :scalar_year], :handle_rdn_rmn_sd_sy),
|
158
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :ordinal_day], :handle_rdn_rmn_od),
|
159
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :ordinal_day, :scalar_year], :handle_rdn_rmn_od_sy),
|
160
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rdn_rmn_sd),
|
161
|
+
Handler.new([:repeater_day_name, :repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rdn_rmn_od),
|
162
|
+
Handler.new([:repeater_day_name, :ordinal_day, :separator_at?, 'time?'], :handle_rdn_od),
|
163
|
+
Handler.new([:scalar_year, [:separator_slash, :separator_dash], :scalar_month, [:separator_slash, :separator_dash], :scalar_day, :repeater_time, :time_zone], :handle_generic),
|
164
|
+
Handler.new([:ordinal_day], :handle_generic),
|
165
|
+
Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),
|
166
|
+
Handler.new([:repeater_month_name, :ordinal_day, :scalar_year], :handle_rmn_od_sy),
|
167
|
+
Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),
|
168
|
+
Handler.new([:repeater_month_name, :ordinal_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_od_sy),
|
169
|
+
Handler.new([:repeater_month_name, [:separator_slash?, :separator_dash?], :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),
|
170
|
+
Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :scalar_day], :handle_rmn_sd_on),
|
171
|
+
Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),
|
172
|
+
Handler.new([:ordinal_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_od_rmn_sy),
|
173
|
+
Handler.new([:ordinal_day, :repeater_month_name, :separator_at?, 'time?'], :handle_od_rmn),
|
174
|
+
Handler.new([:ordinal_day, :grabber?, :repeater_month, :separator_at?, 'time?'], :handle_od_rm),
|
175
|
+
Handler.new([:scalar_year, :repeater_month_name, :ordinal_day], :handle_sy_rmn_od),
|
176
|
+
Handler.new([:repeater_time, :repeater_day_portion?, :separator_on?, :repeater_month_name, :ordinal_day], :handle_rmn_od_on),
|
177
|
+
Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),
|
178
|
+
Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),
|
179
|
+
Handler.new([:scalar_day, [:separator_slash?, :separator_dash?], :repeater_month_name, :separator_at?, 'time?'], :handle_sd_rmn),
|
180
|
+
Handler.new([:scalar_year, [:separator_slash, :separator_dash], :scalar_month, [:separator_slash, :separator_dash], :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),
|
181
|
+
Handler.new([:scalar_year, [:separator_slash, :separator_dash], :scalar_month], :handle_sy_sm),
|
182
|
+
Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_year], :handle_sm_sy),
|
183
|
+
Handler.new([:scalar_day, [:separator_slash, :separator_dash], :repeater_month_name, [:separator_slash, :separator_dash], :scalar_year, :repeater_time?], :handle_sm_rmn_sy),
|
184
|
+
Handler.new([:scalar_year, [:separator_slash, :separator_dash], :scalar_month, [:separator_slash, :separator_dash], :scalar?, :time_zone], :handle_generic),
|
185
|
+
],
|
170
186
|
|
187
|
+
:anchor => [
|
188
|
+
Handler.new([:separator_on?, :grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),
|
189
|
+
Handler.new([:grabber?, :repeater, :repeater, :separator?, :repeater?, :repeater?], :handle_r),
|
190
|
+
Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)
|
191
|
+
],
|
171
192
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
193
|
+
:arrow => [
|
194
|
+
Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),
|
195
|
+
Handler.new([:scalar, :repeater, :separator_and?, :scalar, :repeater, :pointer, :separator_at?, 'anchor'], :handle_s_r_a_s_r_p_a),
|
196
|
+
Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),
|
197
|
+
Handler.new([:scalar, :repeater, :pointer, :separator_at?, 'anchor'], :handle_s_r_p_a)
|
198
|
+
],
|
199
|
+
|
200
|
+
:narrow => [
|
201
|
+
Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),
|
202
|
+
Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)
|
203
|
+
]
|
204
|
+
}
|
205
|
+
|
206
|
+
endians = [
|
207
|
+
Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_day, [:separator_slash, :separator_dash], :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
|
208
|
+
Handler.new([:scalar_month, [:separator_slash, :separator_dash], :scalar_day, :separator_at?, 'time?'], :handle_sm_sd),
|
209
|
+
Handler.new([:scalar_day, [:separator_slash, :separator_dash], :scalar_month, :separator_at?, 'time?'], :handle_sd_sm),
|
210
|
+
Handler.new([:scalar_day, [:separator_slash, :separator_dash], :scalar_month, [:separator_slash, :separator_dash], :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
|
211
|
+
]
|
212
|
+
|
213
|
+
case endian = Array(options[:endian_precedence]).first
|
214
|
+
when :little
|
215
|
+
@@definitions.merge(:endian => endians.reverse)
|
216
|
+
when :middle
|
217
|
+
@@definitions.merge(:endian => endians)
|
218
|
+
else
|
219
|
+
raise ArgumentError, "Unknown endian option '#{endian}'"
|
220
|
+
end
|
177
221
|
end
|
178
222
|
|
223
|
+
private
|
224
|
+
|
179
225
|
def tokenize(text, options)
|
180
226
|
text = pre_normalize(text)
|
181
|
-
tokens =
|
227
|
+
tokens = text.split(' ').map { |word| Token.new(word) }
|
182
228
|
[Repeater, Grabber, Pointer, Scalar, Ordinal, Separator, Sign, TimeZone].each do |tok|
|
183
229
|
tok.scan(tokens, options)
|
184
230
|
end
|
@@ -215,7 +261,7 @@ module Chronic
|
|
215
261
|
end
|
216
262
|
end
|
217
263
|
|
218
|
-
puts
|
264
|
+
puts "-none" if Chronic.debug
|
219
265
|
return nil
|
220
266
|
end
|
221
267
|
end
|