chronic-mmlac 0.6.4.2 → 0.10.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/.travis.yml +8 -0
  4. data/HISTORY.md +69 -0
  5. data/README.md +47 -42
  6. data/Rakefile +28 -8
  7. data/chronic.gemspec +9 -3
  8. data/lib/chronic.rb +113 -74
  9. data/lib/chronic/date.rb +82 -0
  10. data/lib/chronic/grabber.rb +9 -7
  11. data/lib/chronic/handler.rb +47 -40
  12. data/lib/chronic/handlers.rb +210 -28
  13. data/lib/chronic/numerizer.rb +11 -2
  14. data/lib/chronic/ordinal.rb +28 -23
  15. data/lib/chronic/parser.rb +268 -0
  16. data/lib/chronic/pointer.rb +9 -7
  17. data/lib/chronic/repeater.rb +58 -48
  18. data/lib/chronic/repeaters/repeater_day.rb +4 -3
  19. data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
  20. data/lib/chronic/repeaters/repeater_day_portion.rb +29 -14
  21. data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
  22. data/lib/chronic/repeaters/repeater_hour.rb +4 -3
  23. data/lib/chronic/repeaters/repeater_minute.rb +4 -3
  24. data/lib/chronic/repeaters/repeater_month.rb +5 -4
  25. data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
  26. data/lib/chronic/repeaters/repeater_season.rb +5 -3
  27. data/lib/chronic/repeaters/repeater_second.rb +4 -3
  28. data/lib/chronic/repeaters/repeater_time.rb +35 -25
  29. data/lib/chronic/repeaters/repeater_week.rb +4 -3
  30. data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
  31. data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
  32. data/lib/chronic/repeaters/repeater_year.rb +5 -4
  33. data/lib/chronic/scalar.rb +40 -68
  34. data/lib/chronic/season.rb +1 -12
  35. data/lib/chronic/separator.rb +142 -23
  36. data/lib/chronic/sign.rb +49 -0
  37. data/lib/chronic/span.rb +2 -2
  38. data/lib/chronic/tag.rb +10 -15
  39. data/lib/chronic/time.rb +40 -0
  40. data/lib/chronic/time_zone.rb +9 -7
  41. data/lib/chronic/token.rb +16 -10
  42. data/test/helper.rb +7 -1
  43. data/test/{test_Chronic.rb → test_chronic.rb} +69 -34
  44. data/test/{test_DaylightSavings.rb → test_daylight_savings.rb} +1 -1
  45. data/test/{test_Handler.rb → test_handler.rb} +38 -14
  46. data/test/{test_MiniDate.rb → test_mini_date.rb} +9 -9
  47. data/test/{test_Numerizer.rb → test_numerizer.rb} +16 -2
  48. data/test/test_parsing.rb +367 -18
  49. data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +1 -1
  50. data/test/test_repeater_day_portion.rb +254 -0
  51. data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +1 -1
  52. data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +1 -1
  53. data/test/{test_RepeaterMinute.rb → test_repeater_minute.rb} +1 -1
  54. data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +1 -1
  55. data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +1 -1
  56. data/test/{test_RepeaterSeason.rb → test_repeater_season.rb} +1 -1
  57. data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +19 -1
  58. data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +1 -1
  59. data/test/{test_RepeaterWeekday.rb → test_repeater_weekday.rb} +1 -1
  60. data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +1 -1
  61. data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +1 -1
  62. data/test/{test_Span.rb → test_span.rb} +2 -2
  63. data/test/{test_Token.rb → test_token.rb} +1 -1
  64. metadata +107 -46
  65. data/.gemtest +0 -0
  66. data/.yardopts +0 -3
  67. data/lib/chronic/chronic.rb +0 -325
@@ -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
 
@@ -1,31 +1,24 @@
1
1
  module Chronic
2
2
  class Ordinal < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary Ordinal tags to
5
- # each token
4
+ # Scan an Array of Token objects and apply any necessary Ordinal
5
+ # tags to each token.
6
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
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.each do |token|
12
- if t = scan_for_ordinals(token) then token.tag(t) end
13
- if t = scan_for_days(token) then token.tag(t) end
14
- end
15
- end
16
-
17
- # @param [Token] token
18
- # @return [Ordinal, nil]
19
- def self.scan_for_ordinals(token)
20
- Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
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
@@ -0,0 +1,268 @@
1
+ require 'chronic/handlers'
2
+
3
+ module Chronic
4
+ class Parser
5
+ include Handlers
6
+
7
+ # Hash of default configuration options.
8
+ DEFAULT_OPTIONS = {
9
+ :context => :future,
10
+ :now => nil,
11
+ :hours24 => nil,
12
+ :guess => true,
13
+ :ambiguous_time_range => 6,
14
+ :endian_precedence => [:middle, :little],
15
+ :ambiguous_year_future_bias => 50
16
+ }
17
+
18
+ attr_accessor :now
19
+ attr_reader :options
20
+
21
+ # options - An optional Hash of configuration options:
22
+ # :context - If your string represents a birthday, you can set
23
+ # this value to :past and if an ambiguous string is
24
+ # given, it will assume it is in the past.
25
+ # :now - Time, all computations will be based off of time
26
+ # instead of Time.now.
27
+ # :hours24 - Time will be parsed as it would be 24 hour clock.
28
+ # :guess - By default the parser will guess a single point in time
29
+ # for the given date or time. If you'd rather have the
30
+ # entire time span returned, set this to false
31
+ # and a Chronic::Span will be returned. Setting :guess to :end
32
+ # will return last time from Span, to :middle for middle (same as just true)
33
+ # and :begin for first time from span.
34
+ # :ambiguous_time_range - If an Integer is given, ambiguous times
35
+ # (like 5:00) will be assumed to be within the range of
36
+ # that time in the AM to that time in the PM. For
37
+ # example, if you set it to `7`, then the parser will
38
+ # look for the time between 7am and 7pm. In the case of
39
+ # 5:00, it would assume that means 5:00pm. If `:none`
40
+ # is given, no assumption will be made, and the first
41
+ # matching instance of that time will be used.
42
+ # :endian_precedence - By default, Chronic will parse "03/04/2011"
43
+ # as the fourth day of the third month. Alternatively you
44
+ # can tell Chronic to parse this as the third day of the
45
+ # fourth month by setting this to [:little, :middle].
46
+ # :ambiguous_year_future_bias - When parsing two digit years
47
+ # (ie 79) unlike Rubys Time class, Chronic will attempt
48
+ # to assume the full year using this figure. Chronic will
49
+ # look x amount of years into the future and past. If the
50
+ # two digit year is `now + x years` it's assumed to be the
51
+ # future, `now - x years` is assumed to be the past.
52
+ def initialize(options = {})
53
+ @options = DEFAULT_OPTIONS.merge(options)
54
+ @now = options.delete(:now) || Chronic.time_class.now
55
+ end
56
+
57
+ # Parse "text" with the given options
58
+ # Returns either a Time or Chronic::Span, depending on the value of options[:guess]
59
+ def parse(text)
60
+ tokens = tokenize(text, options)
61
+ span = tokens_to_span(tokens, options.merge(:text => text))
62
+
63
+ puts "+#{'-' * 51}\n| #{tokens}\n+#{'-' * 51}" if Chronic.debug
64
+
65
+ guess(span, options[:guess]) if span
66
+ end
67
+
68
+ # Clean up the specified text ready for parsing.
69
+ #
70
+ # Clean up the string by stripping unwanted characters, converting
71
+ # idioms to their canonical form, converting number words to numbers
72
+ # (three => 3), and converting ordinal words to numeric
73
+ # ordinals (third => 3rd)
74
+ #
75
+ # text - The String text to normalize.
76
+ #
77
+ # Examples:
78
+ #
79
+ # Chronic.pre_normalize('first day in May')
80
+ # #=> "1st day in may"
81
+ #
82
+ # Chronic.pre_normalize('tomorrow after noon')
83
+ # #=> "next day future 12:00"
84
+ #
85
+ # Chronic.pre_normalize('one hundred and thirty six days from now')
86
+ # #=> "136 days future this second"
87
+ #
88
+ # Returns a new String ready for Chronic to parse.
89
+ def pre_normalize(text)
90
+ text = text.to_s.downcase
91
+ text.gsub!(/\b(\d{2})\.(\d{2})\.(\d{4})\b/, '\3 / \2 / \1')
92
+ text.gsub!(/\b([ap])\.m\.?/, '\1m')
93
+ text.gsub!(/(\s+|:\d{2}|:\d{2}\.\d{3})\-(\d{2}:?\d{2})\b/, '\1tzminus\2')
94
+ text.gsub!(/\./, ':')
95
+ text.gsub!(/([ap]):m:?/, '\1m')
96
+ text.gsub!(/['"]/, '')
97
+ text.gsub!(/,/, ' ')
98
+ text.gsub!(/^second /, '2nd ')
99
+ text.gsub!(/\bsecond (of|day|month|hour|minute|second)\b/, '2nd \1')
100
+ text = Numerizer.numerize(text)
101
+ text.gsub!(/([\/\-\,\@])/) { ' ' + $1 + ' ' }
102
+ text.gsub!(/(?:^|\s)0(\d+:\d+\s*pm?\b)/, ' \1')
103
+ text.gsub!(/\btoday\b/, 'this day')
104
+ text.gsub!(/\btomm?orr?ow\b/, 'next day')
105
+ text.gsub!(/\byesterday\b/, 'last day')
106
+ text.gsub!(/\bnoon\b/, '12:00pm')
107
+ text.gsub!(/\bmidnight\b/, '24:00')
108
+ text.gsub!(/\bnow\b/, 'this second')
109
+ text.gsub!('quarter', '15')
110
+ text.gsub!('half', '30')
111
+ text.gsub!(/(\d{1,2}) (to|till|prior to|before)\b/, '\1 minutes past')
112
+ text.gsub!(/(\d{1,2}) (after|past)\b/, '\1 minutes future')
113
+ text.gsub!(/\b(?:ago|before(?: now)?)\b/, 'past')
114
+ text.gsub!(/\bthis (?:last|past)\b/, 'last')
115
+ text.gsub!(/\b(?:in|during) the (morning)\b/, '\1')
116
+ text.gsub!(/\b(?:in the|during the|at) (afternoon|evening|night)\b/, '\1')
117
+ text.gsub!(/\btonight\b/, 'this night')
118
+ text.gsub!(/\b\d+:?\d*[ap]\b/,'\0m')
119
+ text.gsub!(/\b(\d{2})(\d{2})(am|pm)\b/, '\1:\2\3')
120
+ text.gsub!(/(\d)([ap]m|oclock)\b/, '\1 \2')
121
+ text.gsub!(/\b(hence|after|from)\b/, 'future')
122
+ text.gsub!(/^\s?an? /i, '1 ')
123
+ text.gsub!(/\b(\d{4}):(\d{2}):(\d{2})\b/, '\1 / \2 / \3') # DTOriginal
124
+ text.gsub!(/\b0(\d+):(\d{2}):(\d{2}) ([ap]m)\b/, '\1:\2:\3 \4')
125
+ text
126
+ end
127
+
128
+ # Guess a specific time within the given span.
129
+ #
130
+ # span - The Chronic::Span object to calcuate a guess from.
131
+ #
132
+ # Returns a new Time object.
133
+ def guess(span, mode = :middle)
134
+ return span if not mode
135
+ return span.begin + span.width / 2 if span.width > 1 and (mode == true or mode == :middle)
136
+ return span.end if mode == :end
137
+ span.begin
138
+ end
139
+
140
+ # List of Handler definitions. See Chronic.parse for a list of options this
141
+ # method accepts.
142
+ #
143
+ # options - An optional Hash of configuration options.
144
+ #
145
+ # Returns a Hash of Handler definitions.
146
+ def definitions(options = {})
147
+ options[:endian_precedence] ||= [:middle, :little]
148
+
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
+ ],
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
+ ],
192
+
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
221
+ end
222
+
223
+ private
224
+
225
+ def tokenize(text, options)
226
+ text = pre_normalize(text)
227
+ tokens = text.split(' ').map { |word| Token.new(word) }
228
+ [Repeater, Grabber, Pointer, Scalar, Ordinal, Separator, Sign, TimeZone].each do |tok|
229
+ tok.scan(tokens, options)
230
+ end
231
+ tokens.select { |token| token.tagged? }
232
+ end
233
+
234
+ def tokens_to_span(tokens, options)
235
+ definitions = definitions(options)
236
+
237
+ (definitions[:endian] + definitions[:date]).each do |handler|
238
+ if handler.match(tokens, definitions)
239
+ good_tokens = tokens.select { |o| !o.get_tag Separator }
240
+ return handler.invoke(:date, good_tokens, self, options)
241
+ end
242
+ end
243
+
244
+ definitions[:anchor].each do |handler|
245
+ if handler.match(tokens, definitions)
246
+ good_tokens = tokens.select { |o| !o.get_tag Separator }
247
+ return handler.invoke(:anchor, good_tokens, self, options)
248
+ end
249
+ end
250
+
251
+ definitions[:arrow].each do |handler|
252
+ if handler.match(tokens, definitions)
253
+ good_tokens = tokens.reject { |o| o.get_tag(SeparatorAt) || o.get_tag(SeparatorSlash) || o.get_tag(SeparatorDash) || o.get_tag(SeparatorComma) || o.get_tag(SeparatorAnd) }
254
+ return handler.invoke(:arrow, good_tokens, self, options)
255
+ end
256
+ end
257
+
258
+ definitions[:narrow].each do |handler|
259
+ if handler.match(tokens, definitions)
260
+ return handler.invoke(:narrow, tokens, self, options)
261
+ end
262
+ end
263
+
264
+ puts "-none" if Chronic.debug
265
+ return nil
266
+ end
267
+ end
268
+ end
@@ -1,20 +1,22 @@
1
1
  module Chronic
2
2
  class Pointer < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary Pointer tags to
5
- # each token
4
+ # Scan an Array of Token objects and apply any necessary Pointer
5
+ # tags to each token.
6
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
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
12
  tokens.each do |token|
12
13
  if t = scan_for_all(token) then token.tag(t) end
13
14
  end
14
15
  end
15
16
 
16
- # @param [Token] token
17
- # @return [Pointer, nil]
17
+ # token - The Token object we want to scan.
18
+ #
19
+ # Returns a new Pointer object.
18
20
  def self.scan_for_all(token)
19
21
  scan_for token, self,
20
22
  {
@@ -1,73 +1,78 @@
1
1
  module Chronic
2
2
  class Repeater < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary Repeater tags to
5
- # each token
4
+ # Scan an Array of Token objects and apply any necessary Repeater
5
+ # tags to each token.
6
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
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
12
  tokens.each do |token|
12
- if t = scan_for_season_names(token) then token.tag(t); next end
13
- if t = scan_for_month_names(token) then token.tag(t); next end
14
- if t = scan_for_day_names(token) then token.tag(t); next end
15
- if t = scan_for_day_portions(token) then token.tag(t); next end
16
- if t = scan_for_times(token) then token.tag(t); next end
17
- if t = scan_for_units(token) then token.tag(t); next end
13
+ if t = scan_for_season_names(token, options) then token.tag(t); next end
14
+ if t = scan_for_month_names(token, options) then token.tag(t); next end
15
+ if t = scan_for_day_names(token, options) then token.tag(t); next end
16
+ if t = scan_for_day_portions(token, options) then token.tag(t); next end
17
+ if t = scan_for_times(token, options) then token.tag(t); next end
18
+ if t = scan_for_units(token, options) then token.tag(t); next end
18
19
  end
19
20
  end
20
21
 
21
- # @param [Token] token
22
- # @return [RepeaterSeasonName, nil]
23
- def self.scan_for_season_names(token)
22
+ # token - The Token object we want to scan.
23
+ #
24
+ # Returns a new Repeater object.
25
+ def self.scan_for_season_names(token, options = {})
24
26
  scan_for token, RepeaterSeasonName,
25
27
  {
26
28
  /^springs?$/ => :spring,
27
29
  /^summers?$/ => :summer,
28
30
  /^(autumn)|(fall)s?$/ => :autumn,
29
31
  /^winters?$/ => :winter
30
- }
32
+ }, options
31
33
  end
32
34
 
33
- # @param [Token] token
34
- # @return [RepeaterMonthName, nil]
35
- def self.scan_for_month_names(token)
35
+ # token - The Token object we want to scan.
36
+ #
37
+ # Returns a new Repeater object.
38
+ def self.scan_for_month_names(token, options = {})
36
39
  scan_for token, RepeaterMonthName,
37
40
  {
38
- /^jan\.?(uary)?$/ => :january,
39
- /^feb\.?(ruary)?$/ => :february,
40
- /^mar\.?(ch)?$/ => :march,
41
- /^apr\.?(il)?$/ => :april,
41
+ /^jan[:\.]?(uary)?$/ => :january,
42
+ /^feb[:\.]?(ruary)?$/ => :february,
43
+ /^mar[:\.]?(ch)?$/ => :march,
44
+ /^apr[:\.]?(il)?$/ => :april,
42
45
  /^may$/ => :may,
43
- /^jun\.?e?$/ => :june,
44
- /^jul\.?y?$/ => :july,
45
- /^aug\.?(ust)?$/ => :august,
46
- /^sep\.?(t\.?|tember)?$/ => :september,
47
- /^oct\.?(ober)?$/ => :october,
48
- /^nov\.?(ember)?$/ => :november,
49
- /^dec\.?(ember)?$/ => :december
50
- }
46
+ /^jun[:\.]?e?$/ => :june,
47
+ /^jul[:\.]?y?$/ => :july,
48
+ /^aug[:\.]?(ust)?$/ => :august,
49
+ /^sep[:\.]?(t[:\.]?|tember)?$/ => :september,
50
+ /^oct[:\.]?(ober)?$/ => :october,
51
+ /^nov[:\.]?(ember)?$/ => :november,
52
+ /^dec[:\.]?(ember)?$/ => :december
53
+ }, options
51
54
  end
52
55
 
53
- # @param [Token] token
54
- # @return [RepeaterDayName, nil]
55
- def self.scan_for_day_names(token)
56
+ # token - The Token object we want to scan.
57
+ #
58
+ # Returns a new Repeater object.
59
+ def self.scan_for_day_names(token, options = {})
56
60
  scan_for token, RepeaterDayName,
57
61
  {
58
62
  /^m[ou]n(day)?$/ => :monday,
59
63
  /^t(ue|eu|oo|u|)s?(day)?$/ => :tuesday,
60
64
  /^we(d|dnes|nds|nns)(day)?$/ => :wednesday,
61
- /^th(u(?:rs)?|ers)(day)?$/ => :thursday,
65
+ /^th(u|ur|urs|ers)(day)?$/ => :thursday,
62
66
  /^fr[iy](day)?$/ => :friday,
63
67
  /^sat(t?[ue]rday)?$/ => :saturday,
64
68
  /^su[nm](day)?$/ => :sunday
65
- }
69
+ }, options
66
70
  end
67
71
 
68
- # @param [Token] token
69
- # @return [RepeaterDayPortion, nil]
70
- def self.scan_for_day_portions(token)
72
+ # token - The Token object we want to scan.
73
+ #
74
+ # Returns a new Repeater object.
75
+ def self.scan_for_day_portions(token, options = {})
71
76
  scan_for token, RepeaterDayPortion,
72
77
  {
73
78
  /^ams?$/ => :am,
@@ -76,18 +81,20 @@ module Chronic
76
81
  /^afternoons?$/ => :afternoon,
77
82
  /^evenings?$/ => :evening,
78
83
  /^(night|nite)s?$/ => :night
79
- }
84
+ }, options
80
85
  end
81
86
 
82
- # @param [Token] token
83
- # @return [RepeaterTime, nil]
84
- def self.scan_for_times(token)
85
- scan_for token, RepeaterTime, /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
87
+ # token - The Token object we want to scan.
88
+ #
89
+ # Returns a new Repeater object.
90
+ def self.scan_for_times(token, options = {})
91
+ scan_for token, RepeaterTime, /^\d{1,2}(:?\d{1,2})?([\.:]?\d{1,2}([\.:]\d{1,6})?)?$/, options
86
92
  end
87
93
 
88
- # @param [Token] token
89
- # @return [Repeater] A new instance of a subclass of Repeater
90
- def self.scan_for_units(token)
94
+ # token - The Token object we want to scan.
95
+ #
96
+ # Returns a new Repeater object.
97
+ def self.scan_for_units(token, options = {})
91
98
  {
92
99
  /^years?$/ => :year,
93
100
  /^seasons?$/ => :season,
@@ -97,14 +104,17 @@ module Chronic
97
104
  /^weekends?$/ => :weekend,
98
105
  /^(week|business)days?$/ => :weekday,
99
106
  /^days?$/ => :day,
107
+ /^hrs?$/ => :hour,
100
108
  /^hours?$/ => :hour,
109
+ /^mins?$/ => :minute,
101
110
  /^minutes?$/ => :minute,
111
+ /^secs?$/ => :second,
102
112
  /^seconds?$/ => :second
103
113
  }.each do |item, symbol|
104
114
  if item =~ token.word
105
115
  klass_name = 'Repeater' + symbol.to_s.capitalize
106
116
  klass = Chronic.const_get(klass_name)
107
- return klass.new(symbol)
117
+ return klass.new(symbol, options)
108
118
  end
109
119
  end
110
120
  return nil
@@ -132,4 +142,4 @@ module Chronic
132
142
  'repeater'
133
143
  end
134
144
  end
135
- end
145
+ end