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
@@ -2,14 +2,15 @@ module Chronic
2
2
  class RepeaterYear < Repeater #:nodoc:
3
3
  YEAR_SECONDS = 31536000 # 365 * 24 * 60 * 60
4
4
 
5
- def initialize(type)
5
+ def initialize(type, options = {})
6
6
  super
7
+ @current_year_start = nil
7
8
  end
8
9
 
9
10
  def next(pointer)
10
11
  super
11
12
 
12
- if !@current_year_start
13
+ unless @current_year_start
13
14
  case pointer
14
15
  when :future
15
16
  @current_year_start = Chronic.construct(@now.year + 1)
@@ -67,11 +68,11 @@ module Chronic
67
68
  end
68
69
 
69
70
  def month_days(year, month)
70
- if Date.leap?(year)
71
+ if ::Date.leap?(year)
71
72
  RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
72
73
  else
73
74
  RepeaterMonth::MONTH_DAYS[month - 1]
74
75
  end
75
76
  end
76
77
  end
77
- end
78
+ end
@@ -2,90 +2,62 @@ module Chronic
2
2
  class Scalar < Tag
3
3
  DAY_PORTIONS = %w( am pm morning afternoon evening night )
4
4
 
5
- # Scan an Array of {Token}s and apply any necessary Scalar tags to
6
- # each token
5
+ # Scan an Array of Token objects and apply any necessary Scalar
6
+ # tags to each token.
7
7
  #
8
- # @param [Array<Token>] tokens Array of tokens to scan
9
- # @param [Hash] options Options specified in {Chronic.parse}
10
- # @return [Array] list of tokens
8
+ # tokens - An Array of tokens to scan.
9
+ # options - The Hash of options specified in Chronic::parse.
10
+ #
11
+ # Returns an Array of tokens.
11
12
  def self.scan(tokens, options)
12
13
  tokens.each_index do |i|
13
- if t = scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
14
- if t = scan_for_days(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
15
- if t = scan_for_months(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
16
- if t = scan_for_years(tokens[i], tokens[i + 1], options) then tokens[i].tag(t) end
17
- end
18
- end
19
-
20
- # @param [Token] token
21
- # @param [Token] post_token
22
- # @return [Scalar, nil]
23
- def self.scan_for_scalars(token, post_token)
24
- if token.word =~ /^\d*$/
25
- unless post_token && DAY_PORTIONS.include?(post_token.word)
26
- return Scalar.new(token.word.to_i)
14
+ token = tokens[i]
15
+ post_token = tokens[i + 1]
16
+ if token.word =~ /^\d+$/
17
+ scalar = token.word.to_i
18
+ token.tag(Scalar.new(scalar))
19
+ token.tag(ScalarSubsecond.new(scalar)) if Chronic::Time::could_be_subsecond?(scalar)
20
+ token.tag(ScalarSecond.new(scalar)) if Chronic::Time::could_be_second?(scalar)
21
+ token.tag(ScalarMinute.new(scalar)) if Chronic::Time::could_be_minute?(scalar)
22
+ token.tag(ScalarHour.new(scalar)) if Chronic::Time::could_be_hour?(scalar)
23
+ unless post_token and DAY_PORTIONS.include?(post_token.word)
24
+ token.tag(ScalarDay.new(scalar)) if Chronic::Date::could_be_day?(scalar)
25
+ token.tag(ScalarMonth.new(scalar)) if Chronic::Date::could_be_month?(scalar)
26
+ if Chronic::Date::could_be_year?(scalar)
27
+ year = Chronic::Date::make_year(scalar, options[:ambiguous_year_future_bias])
28
+ token.tag(ScalarYear.new(year.to_i))
29
+ end
30
+ end
27
31
  end
28
32
  end
29
33
  end
30
34
 
31
- # @param [Token] token
32
- # @param [Token] post_token
33
- # @return [ScalarDay, nil]
34
- def self.scan_for_days(token, post_token)
35
- if token.word =~ /^\d\d?$/
36
- toi = token.word.to_i
37
- unless toi > 31 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
38
- return ScalarDay.new(toi)
39
- end
40
- end
35
+ def to_s
36
+ 'scalar'
41
37
  end
38
+ end
42
39
 
43
- # @param [Token] token
44
- # @param [Token] post_token
45
- # @return [ScalarMonth, nil]
46
- def self.scan_for_months(token, post_token)
47
- if token.word =~ /^\d\d?$/
48
- toi = token.word.to_i
49
- unless toi > 12 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
50
- return ScalarMonth.new(toi)
51
- end
52
- end
40
+ class ScalarSubsecond < Scalar #:nodoc:
41
+ def to_s
42
+ super << '-subsecond-' << @type.to_s
53
43
  end
44
+ end
54
45
 
55
- # @param [Token] token
56
- # @param [Token] post_token
57
- # @param [Hash] options Options specified in {Chronic.parse}
58
- # @return [ScalarYear, nil]
59
- def self.scan_for_years(token, post_token, options)
60
- if token.word =~ /^([1-9]\d)?\d\d?$/
61
- unless post_token && DAY_PORTIONS.include?(post_token.word)
62
- year = make_year(token.word.to_i, options[:ambiguous_year_future_bias])
63
- return ScalarYear.new(year.to_i)
64
- end
65
- end
46
+ class ScalarSecond < Scalar #:nodoc:
47
+ def to_s
48
+ super << '-second-' << @type.to_s
66
49
  end
50
+ end
67
51
 
68
- # Build a year from a 2 digit suffix
69
- #
70
- # @example
71
- # make_year(96, 50) #=> 1996
72
- # make_year(79, 20) #=> 2079
73
- # make_year(00, 50) #=> 2000
74
- #
75
- # @param [Integer] year The two digit year to build from
76
- # @param [Integer] bias The amount of future years to bias
77
- # @return [Integer] The 4 digit year
78
- def self.make_year(year, bias)
79
- return year if year.to_s.size > 2
80
- start_year = Chronic.time_class.now.year - bias
81
- century = (start_year / 100) * 100
82
- full_year = century + year
83
- full_year += 100 if full_year < start_year
84
- full_year
52
+ class ScalarMinute < Scalar #:nodoc:
53
+ def to_s
54
+ super << '-minute-' << @type.to_s
85
55
  end
56
+ end
86
57
 
58
+ class ScalarHour < Scalar #:nodoc:
87
59
  def to_s
88
- 'scalar'
60
+ super << '-hour-' << @type.to_s
89
61
  end
90
62
  end
91
63
 
@@ -1,35 +1,24 @@
1
1
  module Chronic
2
2
  class Season
3
- # @return [MiniDate]
4
- attr_reader :start
5
3
 
6
- # @return [MiniDate]
4
+ attr_reader :start
7
5
  attr_reader :end
8
6
 
9
- # @param [MiniDate] start_date
10
- # @param [MiniDate] end_date
11
7
  def initialize(start_date, end_date)
12
8
  @start = start_date
13
9
  @end = end_date
14
10
  end
15
11
 
16
- # @param [Symbol] season The season name
17
- # @param [Integer] pointer The direction (-1 for past, 1 for future)
18
- # @return [Symbol] The new season name
19
12
  def self.find_next_season(season, pointer)
20
13
  lookup = [:spring, :summer, :autumn, :winter]
21
14
  next_season_num = (lookup.index(season) + 1 * pointer) % 4
22
15
  lookup[next_season_num]
23
16
  end
24
17
 
25
- # @param [Symbol] season The season name
26
- # @return [Symbol] The new season name
27
18
  def self.season_after(season)
28
19
  find_next_season(season, +1)
29
20
  end
30
21
 
31
- # @param [Symbol] season The season name
32
- # @return [Symbol] The new season name
33
22
  def self.season_before(season)
34
23
  find_next_season(season, -1)
35
24
  end
@@ -1,56 +1,126 @@
1
1
  module Chronic
2
2
  class Separator < Tag
3
3
 
4
- # Scan an Array of {Token}s and apply any necessary Separator tags to
5
- # each token
4
+ # Scan an Array of Token objects and apply any necessary Separator
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_commas(token) then token.tag(t); next end
13
- if t = scan_for_slash_or_dash(token) then token.tag(t); next end
14
+ if t = scan_for_dots(token) then token.tag(t); next end
15
+ if t = scan_for_colon(token) then token.tag(t); next end
16
+ if t = scan_for_space(token) then token.tag(t); next end
17
+ if t = scan_for_slash(token) then token.tag(t); next end
18
+ if t = scan_for_dash(token) then token.tag(t); next end
19
+ if t = scan_for_quote(token) then token.tag(t); next end
14
20
  if t = scan_for_at(token) then token.tag(t); next end
15
21
  if t = scan_for_in(token) then token.tag(t); next end
16
22
  if t = scan_for_on(token) then token.tag(t); next end
23
+ if t = scan_for_and(token) then token.tag(t); next end
24
+ if t = scan_for_t(token) then token.tag(t); next end
25
+ if t = scan_for_w(token) then token.tag(t); next end
17
26
  end
18
27
  end
19
28
 
20
- # @param [Token] token
21
- # @return [SeparatorComma, nil]
29
+ # token - The Token object we want to scan.
30
+ #
31
+ # Returns a new SeparatorComma object.
22
32
  def self.scan_for_commas(token)
23
33
  scan_for token, SeparatorComma, { /^,$/ => :comma }
24
34
  end
25
35
 
26
- # @param [Token] token
27
- # @return [SeparatorSlashOrDash, nil]
28
- def self.scan_for_slash_or_dash(token)
29
- scan_for token, SeparatorSlashOrDash,
36
+ # token - The Token object we want to scan.
37
+ #
38
+ # Returns a new SeparatorDot object.
39
+ def self.scan_for_dots(token)
40
+ scan_for token, SeparatorDot, { /^\.$/ => :dot }
41
+ end
42
+
43
+ # token - The Token object we want to scan.
44
+ #
45
+ # Returns a new SeparatorColon object.
46
+ def self.scan_for_colon(token)
47
+ scan_for token, SeparatorColon, { /^:$/ => :colon }
48
+ end
49
+
50
+ # token - The Token object we want to scan.
51
+ #
52
+ # Returns a new SeparatorSpace object.
53
+ def self.scan_for_space(token)
54
+ scan_for token, SeparatorSpace, { /^ $/ => :space }
55
+ end
56
+
57
+ # token - The Token object we want to scan.
58
+ #
59
+ # Returns a new SeparatorSlash object.
60
+ def self.scan_for_slash(token)
61
+ scan_for token, SeparatorSlash, { /^\/$/ => :slash }
62
+ end
63
+
64
+ # token - The Token object we want to scan.
65
+ #
66
+ # Returns a new SeparatorDash object.
67
+ def self.scan_for_dash(token)
68
+ scan_for token, SeparatorDash, { /^-$/ => :dash }
69
+ end
70
+
71
+ # token - The Token object we want to scan.
72
+ #
73
+ # Returns a new SeparatorQuote object.
74
+ def self.scan_for_quote(token)
75
+ scan_for token, SeparatorQuote,
30
76
  {
31
- /^-$/ => :dash,
32
- /^\/$/ => :slash
77
+ /^'$/ => :single_quote,
78
+ /^"$/ => :double_quote
33
79
  }
34
80
  end
35
81
 
36
- # @param [Token] token
37
- # @return [SeparatorAt, nil]
82
+ # token - The Token object we want to scan.
83
+ #
84
+ # Returns a new SeparatorAt object.
38
85
  def self.scan_for_at(token)
39
86
  scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
40
87
  end
41
88
 
42
- # @param [Token] token
43
- # @return [SeparatorIn, nil]
89
+ # token - The Token object we want to scan.
90
+ #
91
+ # Returns a new SeparatorIn object.
44
92
  def self.scan_for_in(token)
45
93
  scan_for token, SeparatorIn, { /^in$/ => :in }
46
94
  end
47
95
 
48
- # @param [Token] token
49
- # @return [SeparatorOn, nil]
96
+ # token - The Token object we want to scan.
97
+ #
98
+ # Returns a new SeparatorOn object.
50
99
  def self.scan_for_on(token)
51
100
  scan_for token, SeparatorOn, { /^on$/ => :on }
52
101
  end
53
102
 
103
+ # token - The Token object we want to scan.
104
+ #
105
+ # Returns a new SeperatorAnd Object object.
106
+ def self.scan_for_and(token)
107
+ scan_for token, SeparatorAnd, { /^and$/ => :and }
108
+ end
109
+
110
+ # token - The Token object we want to scan.
111
+ #
112
+ # Returns a new SeperatorT Object object.
113
+ def self.scan_for_t(token)
114
+ scan_for token, SeparatorT, { /^t$/ => :T }
115
+ end
116
+
117
+ # token - The Token object we want to scan.
118
+ #
119
+ # Returns a new SeperatorW Object object.
120
+ def self.scan_for_w(token)
121
+ scan_for token, SeparatorW, { /^w$/ => :W }
122
+ end
123
+
54
124
  def to_s
55
125
  'separator'
56
126
  end
@@ -62,9 +132,39 @@ module Chronic
62
132
  end
63
133
  end
64
134
 
65
- class SeparatorSlashOrDash < Separator #:nodoc:
135
+ class SeparatorDot < Separator #:nodoc:
66
136
  def to_s
67
- super << '-slashordash-' << @type.to_s
137
+ super << '-dot'
138
+ end
139
+ end
140
+
141
+ class SeparatorColon < Separator #:nodoc:
142
+ def to_s
143
+ super << '-colon'
144
+ end
145
+ end
146
+
147
+ class SeparatorSpace < Separator #:nodoc:
148
+ def to_s
149
+ super << '-space'
150
+ end
151
+ end
152
+
153
+ class SeparatorSlash < Separator #:nodoc:
154
+ def to_s
155
+ super << '-slash'
156
+ end
157
+ end
158
+
159
+ class SeparatorDash < Separator #:nodoc:
160
+ def to_s
161
+ super << '-dash'
162
+ end
163
+ end
164
+
165
+ class SeparatorQuote < Separator #:nodoc:
166
+ def to_s
167
+ super << '-quote-' << @type.to_s
68
168
  end
69
169
  end
70
170
 
@@ -85,4 +185,23 @@ module Chronic
85
185
  super << '-on'
86
186
  end
87
187
  end
88
- end
188
+
189
+ class SeparatorAnd < Separator #:nodoc:
190
+ def to_s
191
+ super << '-and'
192
+ end
193
+ end
194
+
195
+ class SeparatorT < Separator #:nodoc:
196
+ def to_s
197
+ super << '-T'
198
+ end
199
+ end
200
+
201
+ class SeparatorW < Separator #:nodoc:
202
+ def to_s
203
+ super << '-W'
204
+ end
205
+ end
206
+
207
+ end
@@ -0,0 +1,49 @@
1
+ module Chronic
2
+ class Sign < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Sign
5
+ # tags to each token.
6
+ #
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.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ if t = scan_for_plus(token) then token.tag(t); next end
14
+ if t = scan_for_minus(token) then token.tag(t); next end
15
+ end
16
+ end
17
+
18
+ # token - The Token object we want to scan.
19
+ #
20
+ # Returns a new SignPlus object.
21
+ def self.scan_for_plus(token)
22
+ scan_for token, SignPlus, { /^\+$/ => :plus }
23
+ end
24
+
25
+ # token - The Token object we want to scan.
26
+ #
27
+ # Returns a new SignMinus object.
28
+ def self.scan_for_minus(token)
29
+ scan_for token, SignMinus, { /^-$/ => :minus }
30
+ end
31
+
32
+ def to_s
33
+ 'sign'
34
+ end
35
+ end
36
+
37
+ class SignPlus < Sign #:nodoc:
38
+ def to_s
39
+ super << '-plus'
40
+ end
41
+ end
42
+
43
+ class SignMinus < Sign #:nodoc:
44
+ def to_s
45
+ super << '-minus'
46
+ end
47
+ end
48
+
49
+ end