chronic 0.2.2 → 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 +6 -0
- data/.travis.yml +8 -0
- data/HISTORY.md +243 -0
- data/LICENSE +21 -0
- data/README.md +182 -0
- data/Rakefile +62 -15
- data/chronic.gemspec +23 -0
- data/lib/chronic/date.rb +82 -0
- data/lib/chronic/grabber.rb +24 -17
- data/lib/chronic/handler.rb +97 -0
- data/lib/chronic/handlers.rb +490 -312
- data/lib/chronic/mini_date.rb +38 -0
- data/lib/chronic/numerizer.rb +130 -0
- data/lib/chronic/ordinal.rb +33 -24
- data/lib/chronic/parser.rb +268 -0
- data/lib/chronic/pointer.rb +22 -17
- data/lib/chronic/repeater.rb +137 -107
- data/lib/chronic/repeaters/repeater_day.rb +51 -44
- data/lib/chronic/repeaters/repeater_day_name.rb +50 -43
- data/lib/chronic/repeaters/repeater_day_portion.rb +97 -81
- data/lib/chronic/repeaters/repeater_fortnight.rb +61 -54
- data/lib/chronic/repeaters/repeater_hour.rb +51 -44
- data/lib/chronic/repeaters/repeater_minute.rb +51 -44
- data/lib/chronic/repeaters/repeater_month.rb +79 -60
- data/lib/chronic/repeaters/repeater_month_name.rb +85 -83
- data/lib/chronic/repeaters/repeater_season.rb +110 -22
- data/lib/chronic/repeaters/repeater_season_name.rb +41 -22
- data/lib/chronic/repeaters/repeater_second.rb +41 -34
- data/lib/chronic/repeaters/repeater_time.rb +128 -104
- data/lib/chronic/repeaters/repeater_week.rb +65 -58
- data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
- data/lib/chronic/repeaters/repeater_weekend.rb +59 -52
- data/lib/chronic/repeaters/repeater_year.rb +72 -52
- data/lib/chronic/scalar.rb +53 -46
- data/lib/chronic/season.rb +26 -0
- data/lib/chronic/separator.rb +174 -43
- data/lib/chronic/sign.rb +49 -0
- data/lib/chronic/span.rb +31 -0
- data/lib/chronic/tag.rb +37 -0
- data/lib/chronic/time.rb +40 -0
- data/lib/chronic/time_zone.rb +21 -11
- data/lib/chronic/token.rb +51 -0
- data/lib/chronic.rb +94 -69
- data/test/helper.rb +12 -0
- data/test/test_chronic.rb +183 -0
- data/test/test_daylight_savings.rb +118 -0
- data/test/{test_Handler.rb → test_handler.rb} +73 -55
- data/test/test_mini_date.rb +32 -0
- data/test/test_numerizer.rb +86 -0
- data/test/test_parsing.rb +891 -248
- data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +16 -17
- data/test/test_repeater_day_portion.rb +254 -0
- data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +17 -18
- data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +23 -20
- data/test/test_repeater_minute.rb +34 -0
- data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +21 -18
- data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +18 -19
- data/test/test_repeater_season.rb +40 -0
- data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +39 -23
- data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +17 -18
- data/test/test_repeater_weekday.rb +55 -0
- data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +22 -23
- data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +25 -19
- data/test/{test_Span.rb → test_span.rb} +7 -8
- data/test/{test_Token.rb → test_token.rb} +6 -7
- metadata +163 -85
- data/History.txt +0 -49
- data/Manifest.txt +0 -47
- data/README.txt +0 -149
- data/lib/chronic/chronic.rb +0 -239
- data/lib/numerizer/numerizer.rb +0 -103
- data/test/suite.rb +0 -9
- data/test/test_Chronic.rb +0 -50
- data/test/test_Numerizer.rb +0 -48
- data/test/test_Time.rb +0 -50
data/lib/chronic/chronic.rb
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
module Chronic
|
|
2
|
-
class << self
|
|
3
|
-
|
|
4
|
-
# Parses a string containing a natural language date or time. If the parser
|
|
5
|
-
# can find a date or time, either a Time or Chronic::Span will be returned
|
|
6
|
-
# (depending on the value of <tt>:guess</tt>). If no date or time can be found,
|
|
7
|
-
# +nil+ will be returned.
|
|
8
|
-
#
|
|
9
|
-
# Options are:
|
|
10
|
-
#
|
|
11
|
-
# [<tt>:context</tt>]
|
|
12
|
-
# <tt>:past</tt> or <tt>:future</tt> (defaults to <tt>:future</tt>)
|
|
13
|
-
#
|
|
14
|
-
# If your string represents a birthday, you can set <tt>:context</tt> to <tt>:past</tt>
|
|
15
|
-
# and if an ambiguous string is given, it will assume it is in the
|
|
16
|
-
# past. Specify <tt>:future</tt> or omit to set a future context.
|
|
17
|
-
#
|
|
18
|
-
# [<tt>:now</tt>]
|
|
19
|
-
# Time (defaults to Time.now)
|
|
20
|
-
#
|
|
21
|
-
# By setting <tt>:now</tt> to a Time, all computations will be based off
|
|
22
|
-
# of that time instead of Time.now
|
|
23
|
-
#
|
|
24
|
-
# [<tt>:guess</tt>]
|
|
25
|
-
# +true+ or +false+ (defaults to +true+)
|
|
26
|
-
#
|
|
27
|
-
# By default, the parser will guess a single point in time for the
|
|
28
|
-
# given date or time. If you'd rather have the entire time span returned,
|
|
29
|
-
# set <tt>:guess</tt> to +false+ and a Chronic::Span will be returned.
|
|
30
|
-
#
|
|
31
|
-
# [<tt>:ambiguous_time_range</tt>]
|
|
32
|
-
# Integer or <tt>:none</tt> (defaults to <tt>6</tt> (6am-6pm))
|
|
33
|
-
#
|
|
34
|
-
# If an Integer is given, ambiguous times (like 5:00) will be
|
|
35
|
-
# assumed to be within the range of that time in the AM to that time
|
|
36
|
-
# in the PM. For example, if you set it to <tt>7</tt>, then the parser will
|
|
37
|
-
# look for the time between 7am and 7pm. In the case of 5:00, it would
|
|
38
|
-
# assume that means 5:00pm. If <tt>:none</tt> is given, no assumption
|
|
39
|
-
# will be made, and the first matching instance of that time will
|
|
40
|
-
# be used.
|
|
41
|
-
def parse(text, specified_options = {})
|
|
42
|
-
# get options and set defaults if necessary
|
|
43
|
-
default_options = {:context => :future,
|
|
44
|
-
:now => Time.now,
|
|
45
|
-
:guess => true,
|
|
46
|
-
:ambiguous_time_range => 6}
|
|
47
|
-
options = default_options.merge specified_options
|
|
48
|
-
|
|
49
|
-
# ensure the specified options are valid
|
|
50
|
-
specified_options.keys.each do |key|
|
|
51
|
-
default_options.keys.include?(key) || raise(InvalidArgumentException, "#{key} is not a valid option key.")
|
|
52
|
-
end
|
|
53
|
-
[:past, :future, :none].include?(options[:context]) || raise(InvalidArgumentException, "Invalid value ':#{options[:context]}' for :context specified. Valid values are :past and :future.")
|
|
54
|
-
|
|
55
|
-
# store now for later =)
|
|
56
|
-
@now = options[:now]
|
|
57
|
-
|
|
58
|
-
# put the text into a normal format to ease scanning
|
|
59
|
-
text = self.pre_normalize(text)
|
|
60
|
-
|
|
61
|
-
# get base tokens for each word
|
|
62
|
-
@tokens = self.base_tokenize(text)
|
|
63
|
-
|
|
64
|
-
# scan the tokens with each token scanner
|
|
65
|
-
[Repeater].each do |tokenizer|
|
|
66
|
-
@tokens = tokenizer.scan(@tokens, options)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
[Grabber, Pointer, Scalar, Ordinal, Separator, TimeZone].each do |tokenizer|
|
|
70
|
-
@tokens = tokenizer.scan(@tokens)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# strip any non-tagged tokens
|
|
74
|
-
@tokens = @tokens.select { |token| token.tagged? }
|
|
75
|
-
|
|
76
|
-
if Chronic.debug
|
|
77
|
-
puts "+---------------------------------------------------"
|
|
78
|
-
puts "| " + @tokens.to_s
|
|
79
|
-
puts "+---------------------------------------------------"
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# do the heavy lifting
|
|
83
|
-
begin
|
|
84
|
-
span = self.tokens_to_span(@tokens, options)
|
|
85
|
-
rescue
|
|
86
|
-
raise
|
|
87
|
-
return nil
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# guess a time within a span if required
|
|
91
|
-
if options[:guess]
|
|
92
|
-
return self.guess(span)
|
|
93
|
-
else
|
|
94
|
-
return span
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Clean up the specified input text by stripping unwanted characters,
|
|
99
|
-
# converting idioms to their canonical form, converting number words
|
|
100
|
-
# to numbers (three => 3), and converting ordinal words to numeric
|
|
101
|
-
# ordinals (third => 3rd)
|
|
102
|
-
def pre_normalize(text) #:nodoc:
|
|
103
|
-
normalized_text = text.to_s.downcase
|
|
104
|
-
normalized_text = numericize_numbers(normalized_text)
|
|
105
|
-
normalized_text.gsub!(/['"\.]/, '')
|
|
106
|
-
normalized_text.gsub!(/([\/\-\,\@])/) { ' ' + $1 + ' ' }
|
|
107
|
-
normalized_text.gsub!(/\btoday\b/, 'this day')
|
|
108
|
-
normalized_text.gsub!(/\btomm?orr?ow\b/, 'next day')
|
|
109
|
-
normalized_text.gsub!(/\byesterday\b/, 'last day')
|
|
110
|
-
normalized_text.gsub!(/\bnoon\b/, '12:00')
|
|
111
|
-
normalized_text.gsub!(/\bmidnight\b/, '24:00')
|
|
112
|
-
normalized_text.gsub!(/\bbefore now\b/, 'past')
|
|
113
|
-
normalized_text.gsub!(/\bnow\b/, 'this second')
|
|
114
|
-
normalized_text.gsub!(/\b(ago|before)\b/, 'past')
|
|
115
|
-
normalized_text.gsub!(/\bthis past\b/, 'last')
|
|
116
|
-
normalized_text.gsub!(/\bthis last\b/, 'last')
|
|
117
|
-
normalized_text.gsub!(/\b(?:in|during) the (morning)\b/, '\1')
|
|
118
|
-
normalized_text.gsub!(/\b(?:in the|during the|at) (afternoon|evening|night)\b/, '\1')
|
|
119
|
-
normalized_text.gsub!(/\btonight\b/, 'this night')
|
|
120
|
-
normalized_text.gsub!(/(?=\w)([ap]m|oclock)\b/, ' \1')
|
|
121
|
-
normalized_text.gsub!(/\b(hence|after|from)\b/, 'future')
|
|
122
|
-
normalized_text = numericize_ordinals(normalized_text)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Convert number words to numbers (three => 3)
|
|
126
|
-
def numericize_numbers(text) #:nodoc:
|
|
127
|
-
Numerizer.numerize(text)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Convert ordinal words to numeric ordinals (third => 3rd)
|
|
131
|
-
def numericize_ordinals(text) #:nodoc:
|
|
132
|
-
text
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
# Split the text on spaces and convert each word into
|
|
136
|
-
# a Token
|
|
137
|
-
def base_tokenize(text) #:nodoc:
|
|
138
|
-
text.split(' ').map { |word| Token.new(word) }
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# Guess a specific time within the given span
|
|
142
|
-
def guess(span) #:nodoc:
|
|
143
|
-
return nil if span.nil?
|
|
144
|
-
if span.width > 1
|
|
145
|
-
span.begin + (span.width / 2)
|
|
146
|
-
else
|
|
147
|
-
span.begin
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
class Token #:nodoc:
|
|
153
|
-
attr_accessor :word, :tags
|
|
154
|
-
|
|
155
|
-
def initialize(word)
|
|
156
|
-
@word = word
|
|
157
|
-
@tags = []
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Tag this token with the specified tag
|
|
161
|
-
def tag(new_tag)
|
|
162
|
-
@tags << new_tag
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
# Remove all tags of the given class
|
|
166
|
-
def untag(tag_class)
|
|
167
|
-
@tags = @tags.select { |m| !m.kind_of? tag_class }
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Return true if this token has any tags
|
|
171
|
-
def tagged?
|
|
172
|
-
@tags.size > 0
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# Return the Tag that matches the given class
|
|
176
|
-
def get_tag(tag_class)
|
|
177
|
-
matches = @tags.select { |m| m.kind_of? tag_class }
|
|
178
|
-
#matches.size < 2 || raise("Multiple identical tags found")
|
|
179
|
-
return matches.first
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
# Print this Token in a pretty way
|
|
183
|
-
def to_s
|
|
184
|
-
@word << '(' << @tags.join(', ') << ') '
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
# A Span represents a range of time. Since this class extends
|
|
189
|
-
# Range, you can use #begin and #end to get the beginning and
|
|
190
|
-
# ending times of the span (they will be of class Time)
|
|
191
|
-
class Span < Range
|
|
192
|
-
# Returns the width of this span in seconds
|
|
193
|
-
def width
|
|
194
|
-
(self.end - self.begin).to_i
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# Add a number of seconds to this span, returning the
|
|
198
|
-
# resulting Span
|
|
199
|
-
def +(seconds)
|
|
200
|
-
Span.new(self.begin + seconds, self.end + seconds)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Subtract a number of seconds to this span, returning the
|
|
204
|
-
# resulting Span
|
|
205
|
-
def -(seconds)
|
|
206
|
-
self + -seconds
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
# Prints this span in a nice fashion
|
|
210
|
-
def to_s
|
|
211
|
-
'(' << self.begin.to_s << '..' << self.end.to_s << ')'
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# Tokens are tagged with subclassed instances of this class when
|
|
216
|
-
# they match specific criteria
|
|
217
|
-
class Tag #:nodoc:
|
|
218
|
-
attr_accessor :type
|
|
219
|
-
|
|
220
|
-
def initialize(type)
|
|
221
|
-
@type = type
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def start=(s)
|
|
225
|
-
@now = s
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
# Internal exception
|
|
230
|
-
class ChronicPain < Exception #:nodoc:
|
|
231
|
-
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
# This exception is raised if an invalid argument is provided to
|
|
235
|
-
# any of Chronic's methods
|
|
236
|
-
class InvalidArgumentException < Exception
|
|
237
|
-
|
|
238
|
-
end
|
|
239
|
-
end
|
data/lib/numerizer/numerizer.rb
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
require 'strscan'
|
|
2
|
-
|
|
3
|
-
class Numerizer
|
|
4
|
-
|
|
5
|
-
DIRECT_NUMS = [
|
|
6
|
-
['eleven', '11'],
|
|
7
|
-
['twelve', '12'],
|
|
8
|
-
['thirteen', '13'],
|
|
9
|
-
['fourteen', '14'],
|
|
10
|
-
['fifteen', '15'],
|
|
11
|
-
['sixteen', '16'],
|
|
12
|
-
['seventeen', '17'],
|
|
13
|
-
['eighteen', '18'],
|
|
14
|
-
['nineteen', '19'],
|
|
15
|
-
['ninteen', '19'], # Common mis-spelling
|
|
16
|
-
['zero', '0'],
|
|
17
|
-
['one', '1'],
|
|
18
|
-
['two', '2'],
|
|
19
|
-
['three', '3'],
|
|
20
|
-
['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
|
|
21
|
-
['five', '5'],
|
|
22
|
-
['six(\W|$)', '6\1'],
|
|
23
|
-
['seven(\W|$)', '7\1'],
|
|
24
|
-
['eight(\W|$)', '8\1'],
|
|
25
|
-
['nine(\W|$)', '9\1'],
|
|
26
|
-
['ten', '10'],
|
|
27
|
-
['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
TEN_PREFIXES = [ ['twenty', 20],
|
|
31
|
-
['thirty', 30],
|
|
32
|
-
['fourty', 40],
|
|
33
|
-
['fifty', 50],
|
|
34
|
-
['sixty', 60],
|
|
35
|
-
['seventy', 70],
|
|
36
|
-
['eighty', 80],
|
|
37
|
-
['ninety', 90]
|
|
38
|
-
]
|
|
39
|
-
|
|
40
|
-
BIG_PREFIXES = [ ['hundred', 100],
|
|
41
|
-
['thousand', 1000],
|
|
42
|
-
['million', 1_000_000],
|
|
43
|
-
['billion', 1_000_000_000],
|
|
44
|
-
['trillion', 1_000_000_000_000],
|
|
45
|
-
]
|
|
46
|
-
|
|
47
|
-
class << self
|
|
48
|
-
def numerize(string)
|
|
49
|
-
string = string.dup
|
|
50
|
-
|
|
51
|
-
# preprocess
|
|
52
|
-
string.gsub!(/ +|([^\d])-([^d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
|
|
53
|
-
string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
|
|
54
|
-
|
|
55
|
-
# easy/direct replacements
|
|
56
|
-
|
|
57
|
-
DIRECT_NUMS.each do |dn|
|
|
58
|
-
string.gsub!(/#{dn[0]}/i, dn[1])
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# ten, twenty, etc.
|
|
62
|
-
|
|
63
|
-
TEN_PREFIXES.each do |tp|
|
|
64
|
-
string.gsub!(/(?:#{tp[0]})( *\d(?=[^\d]|$))*/i) { (tp[1] + $1.to_i).to_s }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# hundreds, thousands, millions, etc.
|
|
68
|
-
|
|
69
|
-
BIG_PREFIXES.each do |bp|
|
|
70
|
-
string.gsub!(/(\d*) *#{bp[0]}/i) { (bp[1] * $1.to_i).to_s}
|
|
71
|
-
andition(string)
|
|
72
|
-
#combine_numbers(string) # Should to be more efficient way to do this
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# fractional addition
|
|
76
|
-
# I'm not combining this with the previous block as using float addition complicates the strings
|
|
77
|
-
# (with extraneous .0's and such )
|
|
78
|
-
string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
|
|
79
|
-
|
|
80
|
-
string
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
def andition(string)
|
|
85
|
-
sc = StringScanner.new(string)
|
|
86
|
-
while(sc.scan_until(/(\d+)( | and )(\d+)(?=[^\w]|$)/i))
|
|
87
|
-
if sc[2] =~ /and/ || sc[1].size > sc[3].size
|
|
88
|
-
string[(sc.pos - sc.matched_size)..(sc.pos-1)] = (sc[1].to_i + sc[3].to_i).to_s
|
|
89
|
-
sc.reset
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# def combine_numbers(string)
|
|
95
|
-
# sc = StringScanner.new(string)
|
|
96
|
-
# while(sc.scan_until(/(\d+)(?: | and |-)(\d+)(?=[^\w]|$)/i))
|
|
97
|
-
# string[(sc.pos - sc.matched_size)..(sc.pos-1)] = (sc[1].to_i + sc[2].to_i).to_s
|
|
98
|
-
# sc.reset
|
|
99
|
-
# end
|
|
100
|
-
# end
|
|
101
|
-
|
|
102
|
-
end
|
|
103
|
-
end
|
data/test/suite.rb
DELETED
data/test/test_Chronic.rb
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
require 'chronic'
|
|
2
|
-
require 'test/unit'
|
|
3
|
-
|
|
4
|
-
class TestChronic < Test::Unit::TestCase
|
|
5
|
-
|
|
6
|
-
def setup
|
|
7
|
-
# Wed Aug 16 14:00:00 UTC 2006
|
|
8
|
-
@now = Time.local(2006, 8, 16, 14, 0, 0, 0)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def test_post_normalize_am_pm_aliases
|
|
12
|
-
# affect wanted patterns
|
|
13
|
-
|
|
14
|
-
tokens = [Chronic::Token.new("5:00"), Chronic::Token.new("morning")]
|
|
15
|
-
tokens[0].tag(Chronic::RepeaterTime.new("5:00"))
|
|
16
|
-
tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
|
|
17
|
-
|
|
18
|
-
assert_equal :morning, tokens[1].tags[0].type
|
|
19
|
-
|
|
20
|
-
tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
|
|
21
|
-
|
|
22
|
-
assert_equal :am, tokens[1].tags[0].type
|
|
23
|
-
assert_equal 2, tokens.size
|
|
24
|
-
|
|
25
|
-
# don't affect unwanted patterns
|
|
26
|
-
|
|
27
|
-
tokens = [Chronic::Token.new("friday"), Chronic::Token.new("morning")]
|
|
28
|
-
tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
|
|
29
|
-
tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
|
|
30
|
-
|
|
31
|
-
assert_equal :morning, tokens[1].tags[0].type
|
|
32
|
-
|
|
33
|
-
tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
|
|
34
|
-
|
|
35
|
-
assert_equal :morning, tokens[1].tags[0].type
|
|
36
|
-
assert_equal 2, tokens.size
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def test_guess
|
|
40
|
-
span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0))
|
|
41
|
-
assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
|
|
42
|
-
|
|
43
|
-
span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0, 0, 1))
|
|
44
|
-
assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
|
|
45
|
-
|
|
46
|
-
span = Chronic::Span.new(Time.local(2006, 11), Time.local(2006, 12))
|
|
47
|
-
assert_equal Time.local(2006, 11, 16), Chronic.guess(span)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
end
|
data/test/test_Numerizer.rb
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
require 'test/unit'
|
|
2
|
-
require 'chronic'
|
|
3
|
-
|
|
4
|
-
class ParseNumbersTest < Test::Unit::TestCase
|
|
5
|
-
|
|
6
|
-
def test_straight_parsing
|
|
7
|
-
strings = { 1 => 'one',
|
|
8
|
-
5 => 'five',
|
|
9
|
-
10 => 'ten',
|
|
10
|
-
11 => 'eleven',
|
|
11
|
-
12 => 'twelve',
|
|
12
|
-
13 => 'thirteen',
|
|
13
|
-
14 => 'fourteen',
|
|
14
|
-
15 => 'fifteen',
|
|
15
|
-
16 => 'sixteen',
|
|
16
|
-
17 => 'seventeen',
|
|
17
|
-
18 => 'eighteen',
|
|
18
|
-
19 => 'nineteen',
|
|
19
|
-
20 => 'twenty',
|
|
20
|
-
27 => 'twenty seven',
|
|
21
|
-
31 => 'thirty-one',
|
|
22
|
-
59 => 'fifty nine',
|
|
23
|
-
100 => 'a hundred',
|
|
24
|
-
100 => 'one hundred',
|
|
25
|
-
150 => 'one hundred and fifty',
|
|
26
|
-
# 150 => 'one fifty',
|
|
27
|
-
200 => 'two-hundred',
|
|
28
|
-
500 => '5 hundred',
|
|
29
|
-
999 => 'nine hundred and ninety nine',
|
|
30
|
-
1_000 => 'one thousand',
|
|
31
|
-
1_200 => 'twelve hundred',
|
|
32
|
-
1_200 => 'one thousand two hundred',
|
|
33
|
-
17_000 => 'seventeen thousand',
|
|
34
|
-
21_473 => 'twentyone-thousand-four-hundred-and-seventy-three',
|
|
35
|
-
74_002 => 'seventy four thousand and two',
|
|
36
|
-
99_999 => 'ninety nine thousand nine hundred ninety nine',
|
|
37
|
-
100_000 => '100 thousand',
|
|
38
|
-
250_000 => 'two hundred fifty thousand',
|
|
39
|
-
1_000_000 => 'one million',
|
|
40
|
-
1_250_007 => 'one million two hundred fifty thousand and seven',
|
|
41
|
-
1_000_000_000 => 'one billion',
|
|
42
|
-
1_000_000_001 => 'one billion and one' }
|
|
43
|
-
|
|
44
|
-
strings.keys.sort.each do |key|
|
|
45
|
-
assert_equal key, Numerizer.numerize(strings[key]).to_i
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
data/test/test_Time.rb
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
require 'chronic'
|
|
2
|
-
require 'test/unit'
|
|
3
|
-
|
|
4
|
-
class TestTime < Test::Unit::TestCase
|
|
5
|
-
|
|
6
|
-
def setup
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
def test_normal
|
|
10
|
-
assert_equal Time.local(2006, 1, 2, 0, 0, 0), Time.construct(2006, 1, 2, 0, 0, 0)
|
|
11
|
-
assert_equal Time.local(2006, 1, 2, 3, 0, 0), Time.construct(2006, 1, 2, 3, 0, 0)
|
|
12
|
-
assert_equal Time.local(2006, 1, 2, 3, 4, 0), Time.construct(2006, 1, 2, 3, 4, 0)
|
|
13
|
-
assert_equal Time.local(2006, 1, 2, 3, 4, 5), Time.construct(2006, 1, 2, 3, 4, 5)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def test_second_overflow
|
|
17
|
-
assert_equal Time.local(2006, 1, 1, 0, 1, 30), Time.construct(2006, 1, 1, 0, 0, 90)
|
|
18
|
-
assert_equal Time.local(2006, 1, 1, 0, 5, 0), Time.construct(2006, 1, 1, 0, 0, 300)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def test_minute_overflow
|
|
22
|
-
assert_equal Time.local(2006, 1, 1, 1, 30), Time.construct(2006, 1, 1, 0, 90)
|
|
23
|
-
assert_equal Time.local(2006, 1, 1, 5), Time.construct(2006, 1, 1, 0, 300)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def test_hour_overflow
|
|
27
|
-
assert_equal Time.local(2006, 1, 2, 12), Time.construct(2006, 1, 1, 36)
|
|
28
|
-
assert_equal Time.local(2006, 1, 7), Time.construct(2006, 1, 1, 144)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def test_day_overflow
|
|
32
|
-
assert_equal Time.local(2006, 2, 1), Time.construct(2006, 1, 32)
|
|
33
|
-
assert_equal Time.local(2006, 3, 5), Time.construct(2006, 2, 33)
|
|
34
|
-
assert_equal Time.local(2004, 3, 4), Time.construct(2004, 2, 33)
|
|
35
|
-
assert_equal Time.local(2000, 3, 5), Time.construct(2000, 2, 33)
|
|
36
|
-
|
|
37
|
-
assert_nothing_raised do
|
|
38
|
-
Time.construct(2006, 1, 56)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
assert_raise(RuntimeError) do
|
|
42
|
-
Time.construct(2006, 1, 57)
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def test_month_overflow
|
|
47
|
-
assert_equal Time.local(2006, 1), Time.construct(2005, 13)
|
|
48
|
-
assert_equal Time.local(2005, 12), Time.construct(2000, 72)
|
|
49
|
-
end
|
|
50
|
-
end
|