chronic-davispuh 0.10.2.v0.1da32066b3f46f2506b3471e39557497e34afa27

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +3 -0
  5. data/HISTORY.md +243 -0
  6. data/LICENSE +21 -0
  7. data/README.md +185 -0
  8. data/Rakefile +68 -0
  9. data/chronic.gemspec +27 -0
  10. data/lib/chronic.rb +122 -0
  11. data/lib/chronic/arrow.rb +270 -0
  12. data/lib/chronic/date.rb +272 -0
  13. data/lib/chronic/definition.rb +208 -0
  14. data/lib/chronic/dictionary.rb +36 -0
  15. data/lib/chronic/handler.rb +44 -0
  16. data/lib/chronic/handlers/anchor.rb +65 -0
  17. data/lib/chronic/handlers/arrow.rb +84 -0
  18. data/lib/chronic/handlers/date.rb +270 -0
  19. data/lib/chronic/handlers/date_time.rb +72 -0
  20. data/lib/chronic/handlers/general.rb +130 -0
  21. data/lib/chronic/handlers/narrow.rb +54 -0
  22. data/lib/chronic/handlers/time.rb +167 -0
  23. data/lib/chronic/handlers/time_zone.rb +50 -0
  24. data/lib/chronic/objects/anchor_object.rb +263 -0
  25. data/lib/chronic/objects/arrow_object.rb +27 -0
  26. data/lib/chronic/objects/date_object.rb +164 -0
  27. data/lib/chronic/objects/date_time_object.rb +64 -0
  28. data/lib/chronic/objects/handler_object.rb +81 -0
  29. data/lib/chronic/objects/narrow_object.rb +85 -0
  30. data/lib/chronic/objects/time_object.rb +96 -0
  31. data/lib/chronic/objects/time_zone_object.rb +27 -0
  32. data/lib/chronic/parser.rb +154 -0
  33. data/lib/chronic/span.rb +32 -0
  34. data/lib/chronic/tag.rb +84 -0
  35. data/lib/chronic/tags/day_name.rb +34 -0
  36. data/lib/chronic/tags/day_portion.rb +33 -0
  37. data/lib/chronic/tags/day_special.rb +30 -0
  38. data/lib/chronic/tags/grabber.rb +29 -0
  39. data/lib/chronic/tags/keyword.rb +63 -0
  40. data/lib/chronic/tags/month_name.rb +39 -0
  41. data/lib/chronic/tags/ordinal.rb +52 -0
  42. data/lib/chronic/tags/pointer.rb +28 -0
  43. data/lib/chronic/tags/rational.rb +35 -0
  44. data/lib/chronic/tags/scalar.rb +101 -0
  45. data/lib/chronic/tags/season_name.rb +31 -0
  46. data/lib/chronic/tags/separator.rb +130 -0
  47. data/lib/chronic/tags/sign.rb +35 -0
  48. data/lib/chronic/tags/time_special.rb +34 -0
  49. data/lib/chronic/tags/time_zone.rb +56 -0
  50. data/lib/chronic/tags/unit.rb +174 -0
  51. data/lib/chronic/time.rb +141 -0
  52. data/lib/chronic/time_zone.rb +80 -0
  53. data/lib/chronic/token.rb +61 -0
  54. data/lib/chronic/token_group.rb +271 -0
  55. data/lib/chronic/tokenizer.rb +42 -0
  56. data/lib/chronic/version.rb +3 -0
  57. data/test/helper.rb +12 -0
  58. data/test/test_chronic.rb +190 -0
  59. data/test/test_daylight_savings.rb +98 -0
  60. data/test/test_handler.rb +113 -0
  61. data/test/test_parsing.rb +1520 -0
  62. data/test/test_span.rb +23 -0
  63. data/test/test_token.rb +31 -0
  64. metadata +218 -0
@@ -0,0 +1,32 @@
1
+ module Chronic
2
+ # A Span represents a range of time. Since this class extends
3
+ # Range, you can use #begin and #end to get the beginning and
4
+ # ending times of the span (they will be of class Time)
5
+ class Span < Range
6
+ attr_accessor :precision
7
+ # Returns the width of this span in seconds
8
+ def width
9
+ (self.end - self.begin).to_i
10
+ end
11
+
12
+ # Add a number of seconds to this span, returning the
13
+ # resulting Span
14
+ def +(seconds)
15
+ Span.new(self.begin + seconds, self.end + seconds)
16
+ end
17
+
18
+ # Subtract a number of seconds to this span, returning the
19
+ # resulting Span
20
+ def -(seconds)
21
+ self + -seconds
22
+ end
23
+
24
+ # Prints this span in a nice fashion
25
+ def to_s
26
+ '(' << self.begin.to_s << '...' << self.end.to_s << ')'
27
+ end
28
+
29
+ alias :cover? :include? if RUBY_VERSION =~ /^1.8/
30
+
31
+ end
32
+ end
@@ -0,0 +1,84 @@
1
+ module Chronic
2
+ # Tokens are tagged with subclassed instances of this class when
3
+ # they match specific criteria.
4
+ class Tag
5
+
6
+ attr_accessor :type
7
+ attr_accessor :width
8
+
9
+ # type - The Symbol type of this tag.
10
+ def initialize(type, width = nil, options = {})
11
+ @type = type
12
+ @width = width
13
+ @options = options
14
+ end
15
+
16
+ class << self
17
+ # Public: Scan an Array of Token objects.
18
+ #
19
+ # tokens - An Array of tokens to scan.
20
+ # options - The Hash of options specified in Chronic::parse.
21
+ #
22
+ # Returns an Array of tokens.
23
+ def scan(tokens, options)
24
+ raise NotImplementedError, 'Subclasses must override scan!'
25
+ end
26
+
27
+ private
28
+
29
+ # Internal: Match item and create respective Tag class.
30
+ # When item is a Symbol it will match only when it's identical to Token.
31
+ # When it's a String it will case-insesitively match partial token,
32
+ # but only if item's last char have different type than token text's next char.
33
+ # When item is a Regexp it will match by it.
34
+ #
35
+ # item - Item to match. It can be String, Symbol or Regexp.
36
+ # klass - Tag class to create.
37
+ # symbol - Tag type as symbol or string to pass to Tag class.
38
+ # token - Token to match against.
39
+ # options - Options as hash to pass to Tag class.
40
+ #
41
+ # Returns an instance of specified Tag klass or nil if item didn't match.
42
+ def match_item(item, klass, symbol, token, options)
43
+ match = false
44
+ case item
45
+ when String
46
+ item_type = Tokenizer.char_type(item.to_s[-1])
47
+ text_type = token.text[token.position+item.length]
48
+ text_type = Tokenizer.char_type(text_type) if text_type
49
+ compatible = true
50
+ compatible = item_type != text_type if text_type && (item_type == :letter || item_type == :digit)
51
+ match = compatible && token.text[token.position, item.length].casecmp(item).zero?
52
+ when Symbol
53
+ match = token.word == item.to_s
54
+ when Regexp
55
+ match = token.word =~ item
56
+ end
57
+ return klass.new(symbol, nil, options) if match
58
+ nil
59
+ end
60
+
61
+ # Internal: Scan for specified items and create respective Tag class.
62
+ #
63
+ # token - Token to match against.
64
+ # klass - Tag class to create.
65
+ # items - Item(s) to match. It can be Hash, String, Symbol or Regexp.
66
+ # Hash keys can be String, Symbol or Regexp, but values much be Symbol.
67
+ # options - Options as hash to pass to Tag class.
68
+ #
69
+ # Returns an instance of specified Tag klass or nil if item(s) didn't match.
70
+ def scan_for(token, klass, items, options = {})
71
+ if items.kind_of?(Hash)
72
+ items.each do |item, symbol|
73
+ scanned = match_item(item, klass, symbol, token, options)
74
+ return scanned if scanned
75
+ end
76
+ else
77
+ return match_item(items, klass, token.word, token, options)
78
+ end
79
+ nil
80
+ end
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,34 @@
1
+ module Chronic
2
+ class DayName < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary DayName
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
+ token.tag scan_for(token, self, patterns, options)
14
+ end
15
+ end
16
+
17
+ def self.patterns
18
+ @@patterns ||= {
19
+ /^m[ou]n(day)?$/i => :monday,
20
+ /^t(ue|eu|oo|u)s?(day)?$/i => :tuesday,
21
+ /^we(d|dnes|nds|nns)(day)?$/i => :wednesday,
22
+ /^th(u|ur|urs|ers)(day)?$/i => :thursday,
23
+ /^fr[iy](day)?$/i => :friday,
24
+ /^sat(t?[ue]rday)?$/i => :saturday,
25
+ /^su[nm](day)?$/i => :sunday
26
+ }
27
+ end
28
+
29
+ def to_s
30
+ 'dayname-' << @type.to_s
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,33 @@
1
+ module Chronic
2
+ class DayPortion < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary DayPortion
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
+ token.tag scan_for(token, self, patterns, options)
14
+ end
15
+ end
16
+
17
+ def self.patterns
18
+ @@patterns ||= {
19
+ /^ams?$/i => :am,
20
+ /^pms?$/i => :pm,
21
+ 'a.m.' => :am,
22
+ 'p.m.' => :pm,
23
+ 'a' => :am,
24
+ 'p' => :pm
25
+ }
26
+ end
27
+
28
+ def to_s
29
+ 'dayportion-' << @type.to_s
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,30 @@
1
+ module Chronic
2
+ class DaySpecial < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary SeasonName
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
+ token.tag scan_for(token, self, patterns, options)
14
+ end
15
+ end
16
+
17
+ def self.patterns
18
+ @@patterns ||= {
19
+ /^yesterday$/i => :yesterday,
20
+ /^today$/i => :today,
21
+ /^tomorrow$/i => :tomorrow
22
+ }
23
+ end
24
+
25
+ def to_s
26
+ 'dayspecial-' << @type.to_s
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,29 @@
1
+ module Chronic
2
+ class Grabber < Tag
3
+
4
+ # Scan an Array of Tokens and apply any necessary Grabber tags to
5
+ # each token.
6
+ #
7
+ # tokens - An Array of Token objects to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of Token objects.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ token.tag scan_for(token, self, patterns, options)
14
+ end
15
+ end
16
+
17
+ def self.patterns
18
+ @@patterns ||= {
19
+ 'last' => :last,
20
+ 'this' => :this,
21
+ 'next' => :next
22
+ }
23
+ end
24
+
25
+ def to_s
26
+ 'grabber-' << @type.to_s
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ module Chronic
2
+ class Keyword < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Keyword
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
+ token.tag scan_for(token, KeywordAt, { /^(at|@)$/i => :at })
14
+ token.tag scan_for(token, KeywordIn, { 'in' => :in })
15
+ token.tag scan_for(token, KeywordOn, { 'on' => :on })
16
+ token.tag scan_for(token, KeywordAnd, { 'and' => :and })
17
+ token.tag scan_for(token, KeywordTo, { 'to' => :to })
18
+ token.tag scan_for(token, KeywordQ, { :Q => :Q })
19
+ end
20
+ end
21
+
22
+ def to_s
23
+ 'keyword'
24
+ end
25
+ end
26
+
27
+ class KeywordAt < Keyword #:nodoc:
28
+ def to_s
29
+ super << '-at'
30
+ end
31
+ end
32
+
33
+ class KeywordIn < Keyword #:nodoc:
34
+ def to_s
35
+ super << '-in'
36
+ end
37
+ end
38
+
39
+ class KeywordOn < Keyword #:nodoc:
40
+ def to_s
41
+ super << '-on'
42
+ end
43
+ end
44
+
45
+ class KeywordAnd < Keyword #:nodoc:
46
+ def to_s
47
+ super << '-and'
48
+ end
49
+ end
50
+
51
+ class KeywordTo < Keyword #:nodoc:
52
+ def to_s
53
+ super << '-to'
54
+ end
55
+ end
56
+
57
+ class KeywordQ < Keyword #:nodoc:
58
+ def to_s
59
+ super << '-q'
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,39 @@
1
+ module Chronic
2
+ class MonthName < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary MonthName
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
+ token.tag scan_for(token, self, patterns, options)
14
+ end
15
+ end
16
+
17
+ def self.patterns
18
+ @@patterns ||= {
19
+ /^jan[:\.]?(uary)?$/i => :january,
20
+ /^feb[:\.]?(ruary)?$/i => :february,
21
+ /^mar[:\.]?(ch)?$/i => :march,
22
+ /^apr[:\.]?(il)?$/i => :april,
23
+ /^may$/i => :may,
24
+ /^jun[:\.]?e?$/i => :june,
25
+ /^jul[:\.]?y?$/i => :july,
26
+ /^aug[:\.]?(ust)?$/i => :august,
27
+ /^sep[:\.]?(t[:\.]?|tember)?$/i => :september,
28
+ /^oct[:\.]?(ober)?$/i => :october,
29
+ /^nov[:\.]?(ember)?$/i => :november,
30
+ /^dec[:\.]?(ember)?$/i => :december
31
+ }
32
+ end
33
+
34
+ def to_s
35
+ 'monthname-' << @type.to_s
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,52 @@
1
+ module Chronic
2
+ class Ordinal < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Ordinal
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_index do |i|
13
+ if tokens[i].word =~ /^\d+$/ and tokens[i + 1] and tokens[i + 1].word =~ /^st|nd|rd|th|\.$/
14
+ width = tokens[i].word.length
15
+ ordinal = tokens[i].word.to_i
16
+ tokens[i].tag(Ordinal.new(ordinal, width))
17
+ tokens[i].tag(OrdinalDay.new(ordinal, width)) if Chronic::Date::could_be_day?(ordinal, width)
18
+ tokens[i].tag(OrdinalMonth.new(ordinal, width)) if Chronic::Date::could_be_month?(ordinal, width)
19
+ if Chronic::Date::could_be_year?(ordinal, width)
20
+ year = Chronic::Date::make_year(ordinal, options[:ambiguous_year_future_bias])
21
+ tokens[i].tag(OrdinalYear.new(year.to_i, width))
22
+ end
23
+ elsif tokens[i].word =~ /^second$/
24
+ tokens[i].tag(Ordinal.new(2, 1))
25
+ end
26
+ end
27
+ end
28
+
29
+ def to_s
30
+ 'ordinal'
31
+ end
32
+ end
33
+
34
+ class OrdinalDay < Ordinal #:nodoc:
35
+ def to_s
36
+ super << '-day-' << @type.to_s
37
+ end
38
+ end
39
+
40
+ class OrdinalMonth < Ordinal #:nodoc:
41
+ def to_s
42
+ super << '-month-' << @type.to_s
43
+ end
44
+ end
45
+
46
+ class OrdinalYear < Ordinal #:nodoc:
47
+ def to_s
48
+ super << '-year-' << @type.to_s
49
+ end
50
+ end
51
+
52
+ end
@@ -0,0 +1,28 @@
1
+ module Chronic
2
+ class Pointer < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Pointer
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
+ token.tag scan_for(token, self, patterns, options)
14
+ end
15
+ end
16
+
17
+ def self.patterns
18
+ @@patterns ||= {
19
+ /^ago|before|prior|till|to$/i => :past,
20
+ /^future|hence|from|after|past$/i => :future
21
+ }
22
+ end
23
+
24
+ def to_s
25
+ 'pointer-' << @type.to_s
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ module Chronic
2
+ class Rational < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Keyword
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
+ token.tag scan_for(token, RationalQuarter, { 'quarter' => Rational(1, 4) })
14
+ token.tag scan_for(token, RationalHalf, { 'half' => Rational(1, 2) })
15
+ end
16
+ end
17
+
18
+ def to_s
19
+ 'rational'
20
+ end
21
+ end
22
+
23
+ class RationalQuarter < Rational #:nodoc:
24
+ def to_s
25
+ super << '-quarter'
26
+ end
27
+ end
28
+
29
+ class RationalHalf < Rational #:nodoc:
30
+ def to_s
31
+ super << '-half'
32
+ end
33
+ end
34
+
35
+ end